Reputation: 101
I have a media player service that plays music in the background of my app throughout activities like :
public class Music extends Service {
MediaPlayer player;
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onCreate() {
player = MediaPlayer.create(this, R.raw.music);
player.setLooping(true);
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return Service.START_NOT_STICKY;
}
public void onDestroy() {
player.stop();
player.release();
stopSelf();
super.onDestroy();
}
The problem is that when the user changes app or goes to home screen of phone(app goes in the background) the music is still playing.
I have tried to stop it in onStop
and onDestroy
method but this stops the music when I change activities which is something that I don't want(I want the music to keep playing when user navigates through activities).
Update
I tried with broadcast:
I added
LocalBroadcastManager.getInstance(this).registerReceiver(StateReceiver, new IntentFilter("status"));
in onCreate of music Service and a method to receive events :
private BroadcastReceiver StateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String status = intent.getStringExtra("status");
if (parseInt(String.valueOf(status)) == 0) {
player.stop();
}else{player.start();}
}
};
And in Application class I did this:
public class app_class extends Application implements Application.ActivityLifecycleCallbacks {
private static int resumed;
private static int paused;
private static String currentActivity;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
currentActivity = activity.getClass().getSimpleName();
}
public static String getCurrentActivity() {
return currentActivity;
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
send_status(1);
}
@Override
public void onActivityPaused(Activity activity) {
send_status(0);
}
private void send_status(int status_counter) {
Intent intent = new Intent("status");
intent.putExtra("status", String.valueOf(status_counter));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
But the music wont resume
Upvotes: 2
Views: 3380
Reputation: 2090
Can be less boilerplate with LifecycleObserver:
class App : Application(), LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppInBackground() {
sendStatus(0)
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppInForeground() {
sendStatus(1)
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onAppDestroyed() {
sendStatus(2)
}
private fun sendStatus(statusCounter: Int) {
val intent = Intent("status")
intent.putExtra("status", statusCounter)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
}
Add to build.gradle (app)
implementation "androidx.lifecycle:lifecycle-runtime:2.1.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.1.0"
Upvotes: 1
Reputation: 37
Besides using the broadcast receiver as RoyalGriffin answered, the problem with the little interruption of the music can be solve by implementing LifecycleObserver
interface. You can know when the app is in foreground or background, and that way, there is no need of handled the states of every activity.
Implement Application.ActivityLifecycleCallbacks
in your app with his methods
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
and @OnLifecycleEvent(Lifecycle.Event.ON_START)
then add this line to the onCreate()
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
Your app_class would be something like this:
public class app_class extends Application implements LifecycleObserver,
Application.ActivityLifecycleCallbacks{
private static int resumed;
private static int paused;
private static String currentActivity;
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onAppBackgrounded() {
Log.d(TAG, "App in background");
send_status(0, this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onAppForegrounded() {
Log.d(TAG, "App in foreground");
send_status(1, this);
}
@Override
public void onActivityCreated(Activity activity, Bundle
savedInstanceState) {
currentActivity = activity.getClass().getSimpleName();
}
public static String getCurrentActivity() {
return currentActivity;
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
//send_status(1);
}
@Override
public void onActivityPaused(Activity activity) {
//send_status(0);
}
private void send_status(int status_counter) {
Intent intent = new Intent("status");
intent.putExtra("status", String.valueOf(status_counter));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle
outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
Upvotes: 0
Reputation: 2007
You need to prepare the player again after stopping it. If you want to stop the playback when your app goes in background, and start the media from the beginning when your app comes into foreground. Just modify your BroadcastReceiver code to this:
private BroadcastReceiver StateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String status = intent.getStringExtra("status");
if (parseInt(String.valueOf(status)) == 0) {
player.stop();
} else {
player = MediaPlayer.create(this, R.raw.music);
player.setLooping(true);
player.start();
}
}
};
However, if you want to pause the playback and resume it from where you left, then make the following changes: In your Application class, in onActivityDestroyed():
@Override
public void onActivityDestroyed(Activity activity) {
send_status(2);
}
and in the BroadcastReceiver:
private BroadcastReceiver StateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String status = intent.getStringExtra("status");
if (parseInt(String.valueOf(status)) == 0) {
player.pause(); //When app is in background and not killed, we just want to pause the player and not want to lose the current state.
} else if (parseInt(String.valueOf(status)) == 1) {
if (player != null)
player.start(); // If the player was created and wasn't stopped, it won't be null, and the playback will be resumed from where we left of.
else {
// If the player was stopped, we need to prepare it once again.
player = MediaPlayer.create(this, R.raw.music);
player.setLooping(true);
player.start();
}
} else if(player != null){
player.stop();
player.release();
}
}
};
Also, have a look at the MediaPlayer states.
Upvotes: 4
Reputation: 343
You could add this in your application class to check whether your app is in the foreground or not.
public class MyLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private static int resumed;
private static int paused;
private static int started;
private static int stopped;
private static String currentActivity;
public static String getCurrentActivity() {
return currentActivity;
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
currentActivity = activity.getClass().getSimpleName();
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
}
@Override
public void onActivityPaused(Activity activity) {
++paused;
android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
// send broadcast from here to the service
sendBroadcast()
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
}
@Override
public void onActivityStopped(Activity activity) {
++stopped;
android.util.Log.w("test", "application is visible: " + (started > stopped));
}
public static boolean isApplicationVisible() {
return started > stopped;
}
public static boolean isApplicationInForeground() {
return resumed > paused;
}
}
Add this to your application like this :
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new MyLifecycleHandler());
}
}
On receiving the broadcast in service you can stop mediaplayer.
Edit
You need to change your application class to this :
public class app_class extends Application implements Application.ActivityLifecycleCallbacks {
private static int resumed;
private static int paused;
private static String currentActivity;
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
currentActivity = activity.getClass().getSimpleName();
}
public static String getCurrentActivity() {
return currentActivity;
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
send_status(1);
}
@Override
public void onActivityPaused(Activity activity) {
send_status(0);
}
private void send_status(int status_counter) {
Intent intent = new Intent("status");
intent.putExtra("status", String.valueOf(status_counter));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
Upvotes: 1
Reputation:
Have you implemented onDestroy()? If not, I believe that might be the solution - and you stop your Player or whatever you're using to run the service within onDestroy().
A service can be stopped by calling its stopSelf() method, or by calling Context.stopService().
Upvotes: 0