I have an app that has a service that plays music. This is working because the service just keeps on going (and thus playing).
However, when the user presses the home key in any activity I should be able to detect if my application is idle or not. I've been looking around and every question I find has an asnwer like: "use the onPause() or onStop()" but that only works for one activity and copying the code to every activity seems like a dirty solution.
In my main I have tried:
onDestroy: doesn't work when home key pressed.
onPause: stops the music even when starting another intent (not intended).
I have considered detecting the current activity from main but that's a way too dirty solution.
In my main activity I start my service (backgroundMusicPlayer):
#Override
protected void onCreate(Bundle savedInstanceState) {
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
setContentView(R.layout.activity_main);
else
setContentView(R.layout.activity_main_landscape);
//_________BRON(MediaPlayer): http://stackoverflow.com/questions/3369068/android-play-sound-on-button-click-null-pointer-exception
backgroundMusicPlayer = new Intent(MainActivity.this, BackgroundMusicPlayer.class);
startService(backgroundMusicPlayer);
mp = MediaPlayer.create(MainActivity.this, R.raw.buttonclick); //knopgeluiden laden
super.onCreate(savedInstanceState);
}
What should I do?
What you need to know is when your activity is in the background (and when it has returned). There are a few ways to do this, but a reliable way I found was based on this answer.
It works on the premise that using a time reference between activity transitions will most likely provide adequate evidence that an app has been "backgrounded" or not.
This has worked for me and in the callback you can reliably tell your service to do whatever you want.
Also, if your App goes to the background and you're playing, you should then startForeground in your service (so it gains priority) and when the app returns, just stopForeground.
That way you avoid having a foreground service all the time, even when the user is interacting with the app.
Related
I've got a bit of a dilemma and not quite sure how to solve it. Here's the scenario...
I have a multi activity application which plays music from the time it starts to the time the application exits.
However, if I use onPause / onResume to detect when the activity is sent to the background and pause the music in onPause and resume play in onResume, the music "skips" briefly when I start the next activity as the calling activity is finished once the startActivity() is called.
If I don't pause / resume the music in onPause / onResume the music plays smoothly but does NOT stop if the home key is pressed and the activity is sent to the back.
Is there a way to detect an activity is sent to the background (using, say a timer and application flag) without having to use onPause / onResume?
If this is not possible or too hard to implement (I'm still learning as we all are), is there a way to create an "invisible" launcher activity which runs in the background to handle such things but never seen?
As always, thanks in advance.
Turns out there's no simple way around this.
First thing you should do, is move your music streaming to a service, this way it doesn't depend on any activity.
Then you need to tell the service to stop when the entire application is in the background, not when one activity is paused. this fine answer suggests adding a timer to your application, and wait for a couple of seconds after an activity is paused. Of no other activity in the app is resumed - the app is assumed to be in the background.
I wouldn't add a timer to your application, but rather let your music playing service do this (notify the service in each onPause and onResume). Also, two seconds is too long for playing music, I'd start with 500ms and see if it's acceptable.
It's pretty simple IMHO
What you are trying to figure is if the new activity is from your application, continue playing the music, else stop.
You can do this with a boolean flag.
Here's the algorithm:
-boolean flag is set to true in onResume in all activities
-Keep the statements which pause the music in onPause
-Put the above statements (in onPause) in an if(boolean flag)
-whenever an event happens to start a new activity i.e. a button is clicked in your activities, etc, clear the flag (set it to false) in the event listener - eg: the button's onClickListener
This should work
The changeover between activities looks like this:
Activity A onPause
Activity B onCreate (or onRestart if it's already created)
Activity B onStart
Activity B onResume
Activity A onStop
You have a few options how to take advantage of this. You can subclass Application, and keep a boolean in the application class. Make a base Activity that does the following:
In onPause(), call the application to change the boolean to false.
In onResume(), call the application to change the boolean to true.
In onStop(), call the application to stop the music unless the boolean is set to true.
If all of your Activities extend this base Activity, then this will work. When A stops, as long as B was another one of your Activities, it will have resumed and set the boolean to true, so your music will keep playing.
There's a second approach that uses a Bound Service, where you bind in onStart() and unbind in onStop(). You can explore that on your own if you so desire.
You can (and in the fact should) use service to handle the music player. This is the component you are calling as "invisible activity".
If you start it by bindService() method in onResume and unbind in onPause() of each activity in your application it should run all the time. When no activity is bound to the running service the service is stopped by system, so all you need is just stop the music in the onDestroy() or in the onUnbind() method of the service.
Here you have a nice diagram of the Service lifecycle: http://www.tutorialspoint.com/android/android_services.htm
I'm not 100% if automatic management of servers lifecycle will be enough - in such case you can use startService() method to keep the service working all the time, when onUnbind of service is called put some delayed (i.e. using Handler.postDelayed(Runnable r) ) check if after i.e. after 1s service is still unbound and stopSelf() in such case.
OK, here's the solution and it's fairly straightforward once I thought about it in a more logical way.
What I have done is:-
Create a public static int called activityCount.
In the onCreate function of each activity I increment activityCount by 1.
I #Override public void finish() and decrement activityCount by one
and call super.finish().
In onPause if activityCount == 1, pause the music.
In onResume if activityCount == 1, play the music.
This is giving me the desired effect by continuously playing the music but when the home button is clicked the music stops and resumes when the activity is resumed.
Thanks for all the suggestions as it helped me think more logically.
I am needing help to determine the right approach. I want to make a backup of an internal database to a location in the external storage every time the whole application gets interrupted or terminated/destroyed. I want to call this method from a central class called Main which extends Application. The reason for that is, that I need to use several activites and I want to call the backup Method only when needed (like described when the whole application gets destroyed or interrupted by another application). I try to avoid calling this backup method in every activity in their onPause() methods.
I thought about starting a service in the onCreate() method of the application, and starting the backup method when the service gets destroyed. But this won't help in the case of an interrupt, as far as I understood the logic behind services. And also the service doesn't seem to start. startService(new Intent(getApplicationContext(), BackupService.class)); Furthermore I don't think it is a good approach to just use the onDestroy() method of a service, this is not what the service class is made for in my opinion.
So summarizing my Question, do you know a better way then using a service, or if not do you know how I should use the service to be able to call a backup only at the point when the whole app (and not only an activity) is interrupted or destroyed.
First of all, if your service "doesn't seem to start", you are probably doing something wrong.
To accomplish your goal make a backup of an internal database to a location in the external storage every time the whole application gets interrupted or terminated/destroyed:
There are three cases in general here.
If you want to do it in the activity layer:
To know when your application is crashed, you need to implement a custom handler to catch the uncaught exceptions.
To know when your activity is "interrupted", the only way is do it in onPause.
To know when your activity is "terminated", the only way is to do it in onDestroy.
This will require you to have a clear navigation and only do it in your "main activity", and all the other activity starts and comes back to it OR use a flag to indicate if the pause was caused by going to another activity.
If you want to do it in the service layer: (Your way of doing it onDestroy won't allow you to detect interrupted case since you will have to start service sticky to keep it running)
You will have to set up a flag on each activity onBind (you will have to bind it and unbind it) to know if it is a crash/interrupt/termination, which will complicate other part of your code.
To avoid running repetitive code, you will have to create a generic base class and extend your other activities from it.
I use this approach to play background music in one of my games, but I guess it works in this scenario as well.
Use a boolean flag to indicate whether or not your app is launching another part of your app.
boolean movingInApp = false;
....
movingInApp = true;
Intent intent...
.....
public void onPause() {
if(!movingInApp) {
//start service
}
}
public void onResume() {
movingInApp = false;
//Stop service
}
By setting the value of movingInApp to true before launching any intent etc, you can prevent your app from starting the service. Remember to set it to false again later in your onResume() method. If the system makes your app go to the background, this will be false, and your service will be started.
Why dont u have all of your activities extend a base activity which in turn extend the android activity class
I the base activity have backupDB method in the onPause
Therefore u dont have to put it in every activity pause method
I'm developing an android app using bluetooth communication (using a propetary protocol) and I need to catch the moment when the app is killed.
I wanted to use the "onDestroy()" method but it isn't called every time the app is killed.
I noticed that it is called when I press the back button and, only sometimes, when I kill the app from the task manager.
The question is: How can I catch the moment before the app is killed?
Here is the code I tried to use:
#Override
public void onDestroy() {
sendMessage(msg);
Log.d("SampleApp", "destroy");
super.onDestroy();
}
#Override
public void finish(){
sendMessage(msg);
Log.d("SampleApp", "finish");
super.finish();
}
Unfortunately finish() is never called and onDestroy isn't called every time I close the app from the task manager.
How can I handle this?
As stated in the documentation here, there is no guarantee that onDestroy() will ever be called. Instead, use onPause() to do the things you want to do whenever the app moves into the background, and leave only that code in onDestroy() that you want run when your app is killed.
EDIT:
From your comments, it seems that you want to run some code whenever your app goes into the background, but not if it went into the background because you launched an intent. AFAIK, there is no method in Android that handles this by default, but you can use something like this:
Have a boolean like:
boolean usedIntent = false;
Now before using an intent, set the boolean to true. Now in your onPause(), move the code for the intent case into an if block like this one:
if(usedIntent)
{
//Your code
}
Finally, in your onResume(), set the boolean to false again so that it can deal with your app being moved into the background by a non intent means properly.
Your application will not receive any additional callbacks if the process it terminated by external means (i.e. killed for memory reasons or the user Force Stops the application). You will have to make do with the callbacks you received when you app went into the background for your application cleanup.
finish() is only called by the system when the user presses the BACK button from your Activity, although it is often called directly by applications to leave an Activity and return to the previous one. This is not technically a lifecycle callback.
onDestroy() only gets called on an Activity as a result of a call to finish(), so mainly only when the user hits the BACK button. When the user hits the HOME button, the foreground Activity only goes through onPause() and onStop().
This means that Android doesn't provide much feedback to an Activity to differentiate a user going Home versus moving to another Activity (from your app or any other); the Activity itself simply knows it's no longer in the foreground. An Android application is more a loose collection of Activities than it is a tightly integrated singular concept (like you may be used to on other platforms) so there are no real system callbacks to know when your application as a whole has been brought forward or moved backward.
Ultimately, I would urge you to reconsider your application architecture if it relies on the knowledge of whether ANY Activity in your application is in the foreground, but depending on your needs, there may be other ways more friendly to the framework to accomplish this. One option is to implement a bound Service inside of your application that every Activity binds to while active (i.e. between onStart() and onStop()). What this provides you is the ability to leverage the fact that a bound Service only lives as long as clients are bound to it, so you can monitor the onCreate() and onDestroy() methods of the Service to know when the current foreground task is not part of your application.
You might also find this article written by Dianne Hackborn to be interesting covering in more detail the Android architecture and how Google thinks it ought to be used.
I just resolved a similar kind of issue.
Here is what you can do if its just about stopping service when application is killed by swiping from Recent app list.
Inside your Manifest file, keep flag stopWithTask as true for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="true" />
But as you say you want to unregister listeners and stop notification etc, I would suggest this approach:
Inside your Manifest file, keep flag stopWithTask as false for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="false" />
Now in your MyService service, override method onTaskRemoved. (This will be fired only if stopWithTask is set to false).
public void onTaskRemoved(Intent rootIntent) {
//unregister listeners
//do any other cleanup if required
//stop service
stopSelf();
}
Refer this question for more details, which contains other part of code, too.
Start service like below
startService(new Intent(this, MyService.class));
Hope this helps.
My application has some background music playing on a loop, using a MediaPlayer. I stop the music in the onPause of the Activity so it doesn't keep on playing if the user leaves the application by pressing the Home key, or if they receive a phone call, etc.
However, I now need the music to continue playing seamlessly when the user moves to certain other activities. That is, if they press button A then I add activity A to the stack and the music should continue; however if they press button B then I add activity B to the stack and the music should stop. Similarly the music should continue if they press the phone's Back button to return from activity A to the original activity.
I've tried starting the MediaPlayer again in the onResume of Activity A, but there's quite a noticeable gap in the music during the transition.
At the moment I've got onPause triggering a half-second delay before fading the music out over another quarter of a second; this can be cancelled from within another activity's onResume. This means the music stops just about quickly enough when the user's leaving the app; however when the user switches activities I still get a slight pause in the music on some slower devices. Furthermore it feels like a really dirty hack.
Is there a better way?
[Edit: if it helps, the MediaPlayer is held in a static class that I can access from anywhere in the application.]
The simplest solution that I can think of offhand if I needed to get this done would be to set a static flag variable in my global Application object (let's call it sStartingNewActivity). Wherever I'm starting an intent new activity (or pressing "back" from an activity that isn't the entry point, overriding the onBackPressed method), I'd set that value to true, and have onPause not stop the music if that flag is set. I'd set the value back to false in every activity's onCreate. I don't think a 1-2-second-pause-before-fadeout is a bad way to go, though -- that would actually behave pretty similarly to how it works when the user presses the "home" button in an iOS app that has background music.
EDIT: You can also try a service-based solution that "sniffs" for whether your activities are running by having your activities listen for a specific broadcast; there's some sample code online that I haven't tried but it's an interesting approach).
I have an app that starts playing sounds and begins/resumes gameplay in the onResume() method, but what I'm noticing is that if my app was the last run application when I put the phone into standby (screen off), and I just press the Menu button to check the time, then the phone starts playing the game and sounds in the background (the app isn't actually visible, only the screen with the date/time is, yet onResume must have been called in my app). What am I to do here? Is there a way to discern what is reactivating the app, and then add a conditional statement that only starts the game when the app is actually visible?
Here is a snippet from my onResume:
#Override
protected void onResume()
{
mySaveGame = Utilities.loadSavegame(this);
//check the savegame
if(mySaveGame!=null)
{
//start game using savegame values
this.startFromSavedGame(mySaveGame.getIsLevelComplete());
}
else
{
//run the 1st-run components
this.startFirstRun();
}
super.onResume();
}
The only thing I can think of doing to prevent the game from starting whenever the screen gets turned on (even when the app isn't visible) is to put this.finish() as the last line in onPause()... but that forces you to restart the app every time you want to go back to it because the precess itself was killed (which is fine because my onPause saves persistent data, but it's not an elegant solution).
Please help.
Have you considered switching to onStart() and onStop(), rather than onResume() and onPause()?
I was having the same problem (I had my music player resume/pause at onResume()/onPause()) and the best solution I found is to pause and resume my activity when it is on the foreground which you can get with public void onWindowFocusChanged (boolean hasFocus) callback.
Edit: This in an old and slightly incorrect answer - correct response is described in the Android Developers Blog: making android games that play nice