We
don’t have any direct approach to find whether our application gone to
background but we have few interfaces from android which helps to solve this.
The
following interfaces which help us are:
Both
the interfaces are added in API level 14.
Application.ActivityLifecycleCallbacks: This interface helps us to trigger
the life cycle of all activities. We can use this ActivityLifecycleCallbacks
interface in the Custom Application class, So that we can trigger all the
activity life cycles @ one location.
ComponentCallbacks2: This class extended
ComponentCallbacks
interface with a
new callback for finer-grained memory management. This interface is available
in all application components (Activity
, Service
, ContentProvider
, and Application
).
The public method onTrimMemory triggers when the operating system has determined
that it is a good time for a process to trim unneeded memory from its process.
This will happen for example when it goes in the background and
there is not enough memory to keep as many background processes running as
desired.
Normally,
onTrimMemory will
be triggered when ever application goes to background, UI should be released at
this point to allow memory to be better managed but we should never compare to exact
values of the level, since new intermediate values may be added -- we will
typically want to compare if the value is greater or equal to a level you are
interested in.
Constants
|
|||
Return type
|
Level
|
Description
|
Constant Value
|
int
|
Level for onTrimMemory(int): the process has gone on to the
LRU list. This is a good opportunity to clean up resources that can
efficiently and quickly be re-built if the user returns to the app.
|
40
|
|
int
|
Level for onTrimMemory(int): the process is nearing the end
of the background LRU list, and if more memory isn't found soon it will be
killed.
|
80
|
|
int
|
Level for onTrimMemory(int): the process is around the
middle of the background LRU list; freeing memory can help the system keep
other processes running later in the list for better overall performance.
|
60
|
|
int
|
Level for onTrimMemory(int): the process is not an
expendable background process, but the device is running extremely low on
memory and is about to not be able to keep any background processes running.
|
15
|
|
int
|
Level for onTrimMemory(int): the process is not an
expendable background process, but the device is running low on memory.
|
10
|
|
int
|
Level for onTrimMemory(int): the process is not an
expendable background process, but the device is running moderately low on
memory.
|
5
|
|
int
|
Level for onTrimMemory(int): the process had been showing a
user interface, and is no longer doing so. Large allocations with the UI
should be released at this point to allow memory to be better managed.
|
20
|
By
using the above 2 interfaces, we can write our logic to trigger when
application goes to background in the custom application class.
Create
AppStatusApplication.java
class which extends Application.java
and two interfaces classes ActivityLifecycleCallbacks,
ComponentCallbacks2 as follows
AppStatusApplication.java
import
android.annotation.TargetApi;
import
android.app.Activity;
import
android.app.Application;
import
android.app.Application.ActivityLifecycleCallbacks;
import
android.content.BroadcastReceiver;
import
android.content.ComponentCallbacks2;
import
android.content.Context;
import android.content.Intent;
import
android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
/**
* AppStatusApplication helps us to find,
whether the application came to
* foreground or not by using interfaces like
ActivityLifecycleCallbacks,
* ComponentCallbacks2 and Broadcast receiver.
*
* @author Vardhan
*
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class AppStatusApplication
extends Application implements
ActivityLifecycleCallbacks,
ComponentCallbacks2 {
private static String TAG =
AppStatusApplication.class.getName();
public static String stateOfLifeCycle = "";
public static boolean wasInBackground = false;
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
...
}
@Override
public void
onActivityCreated(Activity activity, Bundle arg1) {
wasInBackground = false;
stateOfLifeCycle = "Create";
}
@Override
public void
onActivityStarted(Activity activity) {
stateOfLifeCycle = "Start";
}
@Override
public void
onActivityResumed(Activity activity) {
stateOfLifeCycle = "Resume";
}
@Override
public void
onActivityPaused(Activity activity) {
stateOfLifeCycle = "Pause";
}
@Override
public void
onActivityStopped(Activity activity) {
stateOfLifeCycle = "Stop";
}
@Override
public void
onActivitySaveInstanceState(Activity activity, Bundle arg1) {
}
@Override
public void
onActivityDestroyed(Activity activity) {
wasInBackground = false;
stateOfLifeCycle = "Destroy";
}
@Override
public void onTrimMemory(int level) {
if (stateOfLifeCycle.equals("Stop")) {
wasInBackground = true;
}
super.onTrimMemory(level);
}
...
}
Then create
two activities to check whether application will trigger when app comes to
foreground or not.
Here my two activities are MainActivity.java
and NavigatedActivity.java
MainActivity.java
import
android.app.Activity;
import
android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import
android.view.View.OnClickListener;
import
android.widget.Button;
import
android.widget.Toast;
/**
* MainActivity is used to check the life cycle
flow and to test the application
* status.
*
* @author Vardhan
*
*/
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getName();
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, TAG + "
onCreate");
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, TAG + "
onStart");
if
(AppStatusApplication.wasInBackground) {
Toast.makeText(getApplicationContext(),
"Application
came to foreground", Toast.LENGTH_SHORT)
.show();
AppStatusApplication.wasInBackground = false;
}
((Button)
findViewById(R.id.btn))
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
startActivity(new
Intent(getApplicationContext(),
NavigatedActivity.class));
}
});
}
}
NavigatedActivity.java
import
android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import
android.widget.Toast;
/**
* NavigatedActivity is used to check the life
cycle flow when we shift between
* activities.
*
* @author Vardhan
*
*/
public class NavigatedActivity extends Activity {
private static final String TAG = NavigatedActivity.class.getName();
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, TAG + "
onCreate");
setContentView(R.layout.navigated_main);
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, TAG + "
onStart");
if
(AppStatusApplication.wasInBackground) {
Toast.makeText(getApplicationContext(),
"Application
came to foreground", Toast.LENGTH_SHORT)
.show();
AppStatusApplication.wasInBackground = false;
}
}
}
By using
this three classes we can trigger whether application come to foreground or not.
If User
wants to trigger @ screen switch off then we can use broadcast receiver in the
custom application.
// Initiate
ScreenOffReceiver screenOffReceiver = new
ScreenOffReceiver();
// declare in onCreate class of
AppStatusApplication.java
registerReceiver(screenOffReceiver, new IntentFilter(
"android.intent.action.SCREEN_OFF"));
// Inner Class
class ScreenOffReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
wasInBackground = true;
}
}
Screen Shot:
Source
Code
You can download the source code by clicking here: AppStatusUsingActivityCallBacksICS-SourceCode. This project is built using eclipse IDE. Unzip
and import the project into Eclipse, it’s a good idea to use the Project by clean
and rebuild from the project menu. It works in all API levels above API 14.
Have
something to add to this post? Share it in the comments.
Hello. Your tutorial was really helpful to me. But I need your help at one instance.
ReplyDeleteIn the mainActivity's button onClick, I have written the following code
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + "1234"));
startActivity(intent);
Now, the problem is I get an exception: Performing pause of activity that is not resumed (MainActivity).
I am completely perplexed as to why is this coming.
It seems that after the onStop, onPause is called which is not true ideally.
Can you suggest me something onto this end?
Hi AnujaKanchan,
DeleteI am not getting the error/ exception you got.
If possible, provide me the source code you have tested.
Regards
Vardhan
If you launch a generic intent and there isn't any application which can handle it, the application will crash. Check this article from Google developers:
Deletehttp://developer.android.com/guide/components/intents-filters.html#ExampleSend
Thank you for sharing this.
ReplyDeleteThe onStop method is called after the on trim method so the checked state of lifecycle in onTrim is never "Stop"
ReplyDeleteHello Vardhan, I like your way. But my application need to be compatible from API 8/9 . Can we use it for older version, is there any support library? I have found https://github.com/BoD/android-activitylifecyclecallbacks-compat. Did you tried it?
ReplyDeleteThere is no direct approach for below version...Only way is having common super class and achieving...You can check this post too http://vardhan-justlikethat.blogspot.in/2013/05/android-solution-to-detect-when-android.html
DeleteWhy are we not using TRIM_MEMORY_UI_HIDDEN ? why do we have to keep track of life cycle . As of the previous commenters mentioned we cannot assure on onTrimMemory being called after onStop . Wanted to know if there is any downside in using TRIM_MEMORY_UI_HIDDEN
ReplyDeleteVardhan I have successfully implemented your code but I want to avoid onBackPressed which too may call activity form background and I just want to fire event when the app actually goes to background by pressing home button or opening of other apps... Any help will save my life
ReplyDeleteyou can directly override onbackpressed to handle the case
DeleteI'm new in android May i know,What are the difference between these and How it use in android code. OnTrimmemory(),System.gc()Finalize().
ReplyDeleteif (stateOfLifeCycle.equals("Stop")) {
ReplyDeletewasInBackground = true;
}
super.onTrimMemory(level);
Log.d(TAG, "onTrimMemory " + level);
this don't works for me.
onActivityPaused MainActivity always before onTrimMemory