I want to detect when my application is sent to the background. There are many questions about hooking the HOME key - I understand this is possible only by registering as a launcher app.
...BUT... as always there is a client who wants certain behaviour...
We have an app with high security requirements. The client wants the app to log out of the server whenever the app goes into the background for whatever reason (phone call, HOME key, back on last activity) (* *to clarify I mean that when the front Activity on the screen is not one of my app's activities **).
So, if I can't hook the HOME key, what other options are there? Obviously just hooking onPause() won't help, because that is Activity-specific.
The "best" we have come up with is to keep an array of Activity references in our Application class. In each Activity's onResume() we add it to this array. In onPause() we remove it. Also in onPause() we enumerate through this array to find out if any of the registered activities are in the foreground. If no foreground activity is found, user gets logged out.
I am unhappy with this as a solution, and hope to find a better way.
// use service
// in that
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_CALL);
filter.addAction(Intent.ACTION_ANSWER);
registerReceiver(mIntentReceiver, filter);
}
// then in BroadcastReceiver
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equalsIgnoreCase("android.intent.category.HOME") )
{
//logout logic
}
else if(action.equalsIgnoreCase("android.intent.action.SCREEN_OFF") )
{
//logout logic
}
else if(action.equalsIgnoreCase("android.intent.action.DIAL") )
{
//logout logic
}
else if(action.equalsIgnoreCase("android.intent.action.CALL")){
/ /logout logic
}
}
We eneded up going for something based on solution here by #peceps: Run code when Android app is closed/sent to background.
I handled it by storing a timestamp when my activity closes\pauses. When another activity starts, it reads the timestamp and if it varies by more than x seconds I perform the log out.
If you need to physically perform a logout (i.e on a remote server), set up AlarmManager when the activity pauses to logout x seconds in the future. You can cancel this Alarm if another activity starts before it fires.
Or you could use a Shared Object who's a singleton, and create single onPause() and onResume() that will get/set the data on that shared object. Those functions will be used in all activities' onPause and onResume.
I worked before to solve same problem but there was no way to do it as I know except your way using now. and best overriding method to catch activity's showing status is onStart(), onStop() this method catchs real visibility change and count your activitys stack count to logout.
This should help:
This s a method found in the Activity class
protected void onUserLeaveHint ()
Since: API Level 3
Called as part of the activity lifecycle when an activity is about to go into the background as the result of user choice. For example, when the user presses the Home key, onUserLeaveHint() will be called, but when an incoming phone call causes the in-call Activity to be automatically brought to the foreground, onUserLeaveHint() will not be called on the activity being interrupted. In cases when it is invoked, this method is called right before the activity's onPause() callback.
This callback and onUserInteraction() are intended to help activities manage status bar notifications intelligently; specifically, for helping activities determine the proper time to cancel a notfication.
I don't know if its going to help you out but I will try in this manner
Create a base Activity and override its onStop() method to logout from server
All Activities of my app will extend above base class.
So now what happens if on any Activity goes in onStop condition it will logout from server no matter how the Activity goes into background
Note: But if you from your code stops any Activity by calling finish then also it will logout so you have to work out in that scenario
android:clearTaskOnLaunch might be helpful. It does not log you out when you go to background, but you can force login screen to be the first one when you just returning back.
Related
I understand the concept of the Android lifecycle and the different events involved. I have created a notification service to let the user know when it's his/her turn. I only want this to execute when the user is no longer viewing one of the activities within the application. He/she will know if it's his/her turn while using the application.
Questions:
If I setup the lifecycle events on the Main.java class, what happens when the user navigates to a different class? Do the onPause events fire? How does the system know which onPause events to execute since the user could be leaving 10 or more activities during a single session?
I am currently starting the service and stopping the service during the onStop and onRestart events in the Main.java class. This is not working as expected. The notifications occur even when I'm in the system, which is causing the system to lockup because of simultaneous calls to the external apis. (see below).
public void onRestart() {
super.onRestart();
Intent i = new Intent(Main.this, NotifyService.class);
Main.this.stopService(i);
}
public void onStop() {
super.onStop();
Intent i = new Intent(Main.this, NotifyService.class);
i.putExtra("UserId", userId);
Main.this.startService(i);
}
Any help is appreciated.
Say you are in Activity-A, and you move to Activity-B, then lifecycle callbacks of Activty-A will be called - in this case Activity-A.onPause() immediately, and Activity-A.onDestroy() maybe in a shortwhile if Activity-A is destroyed in that while.
The correct methods to use for starting/stopping your service are onResume() (instead of the onRestart() that you use) and onPause() (instead of the onStop() that you use)
You can set up a counter, increment it in Activity's onResume() and decrement on onPause(). If your counter is 0 then it means user is NOT in your activity (is using other app). If it is not 1, then s/he is using your app. Note your counter should be kept outside of activity as it may be removed. I subclass Application class and keep my counters of that global scope there.
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.
I.e I would like to know when user interact with my application and when not.
I have tried do it using ActivityManager.getRecentTasks(). I have checked root activity at a top task to detect interact user with my application or not.
I have forced to check it in separated thread each second or two.
This way is bad for me. There is another way to detect when any activity of my app are opening or closed?
Have a look at the lifecycle of an Activity.
There are callback methods (onStart, onResume, onPause, onDestroy, ...) that are invoked by the system whenever your activity is created, becomes active or inactive etc.
You might create your own application class (just inherit from android.app.Application) and do your tracking there. The application will be around as long as your app is running.
For example you could put a flag or a counter there and set it from the activities' callbacks. A simple example for that could be:
public void onResume() {
super.onResume();
((MyApplication)getApplication()).active = true;
}
public void onDestroy() {
super.onDestroy();
((MyApplication)getApplication()).destroyed += 1;
}
Is there any way to get different event for application in background and particular activity in background?
In other words when any activity goes to background the onPause() method called, is there any to find that whole application goes to background?
Is there any settings for Manifest file to close the application when its goes to background.
Thanks,
AndroidIT
To identify if the application is in background:
There is a much more simpler approach:
On a BaseActivity that all Activities extend:
protected static boolean isVisible = false;
#Override
public void onResume()
{
super.onResume();
setVisible(true);
}
#Override
public void onPause()
{
super.onPause();
setVisible(false);
}
Whenever you need to check if any of your application activities is in foreground just check isVisible();
To understand this approach check this answer of side-by-side activity lifecycle: Activity side-by-side lifecycle
If you want to kill your application when it goes to background you have several options:
Something like this: Remove or close your own Activity window from a Status Bar Notification Intent modified for what you want.
Explore: android:finishOnTaskLaunch and android:excludeFromRecents and how you can make logic to make this effect (have done it already)
I would assume that all your activities are launched by one of the other activities in your app. If that is the case perhaps your activities could set flag indicating it is launching an activity. Then when onpause() is called this flag will determine if the app is being move to the background or simply launching the next activity.
save the state of each activity in shared preferences. each activity will need its own field in the preference file. each time you onResume or onPause, update the correct preference.
or: every time you onResume or onPause send a service a notification via intent that X activity has paused/resumed. the service will keep track of application state this way
I have an issue. For analytic purposes I need to track when the APP (not activity) is resumed. The problem I have now is that if I put the tracker on the OnResume event of an activity, it will get fired every time the user goes back and forth on different activities.
How can I avoid that? How can I track the real "Application Resume," (when user actually exits the app and come back) and not the activity resume?
Any ideas is greatly appreciated. Thanks.
I encountered the same problem and solved it by creating base activity :
public class mActivity extends Activity{
public static final String TAG = "mActivity";
public static int activities_num = 0;
#Override
protected void onStop() {
super.onStop();
activities_num--;
if(activities_num == 0){
Log.e(TAG,"user not longer in the application");
}
}
#Override
protected void onStart() {
super.onStart();
activities_num++;
}
}
all the other activities in my app inherited mActivity. When an activity is no longer visible than onStop is called. when activities_num == 0 than all activities are not visible (meaning the the user close the app or it passed to the background). When the user start the application (or restarting it from the background) onStart will be called (onStart is called when the activity is visible) and activities_num > 0. hopes it helps...
Use the Application object of your app (see http://developer.android.com/reference/android/app/Application.html). If you create a custom Application class and configure it in your AndroidManifest.xml file you can do something like this:
Start tracking in the onCreate() of the Application object.
Instrument all your Activities so their onPause() and onResume() methods check with the Application object and see if they are the first Activity to run, or if they are continuing a previously running instance of the app.
Stop tracking in the onDestroy() of the Application object.
To a certain degree most of the analytics packages (Flurry and their ilk) do something similar to this. You'll need to do a little state machine work to get this to work right, but it shouldn't be too complicated.
Instead of OnResume(), hook into the OnCreate() event of your main activity.