38

I have an Android application which sends some data to a webservice. I need to send this data as soon as the application is closed or sent to background.. But how do I accomplish this?

My current solution is to run it on the OnPause() on my home activity, but I need this to run no matter which activity the user is on when closing the app.. Is this possible or do I have to add the OnPause method to all activities?

Linora
  • 10,418
  • 9
  • 38
  • 49
  • I think a Service performs long-term operations and is enabled when the user quits the application. – Utopia Dec 14 '16 at 09:44

8 Answers8

25

Check this solution first https://stackoverflow.com/a/5862048/1037294 before you decide to use the code below!


To check if your application is sent to background, you can call this code on onPause() or onStop() on every activity in your application:

 /**
   * Checks if the application is being sent in the background (i.e behind
   * another application's Activity).
   * 
   * @param context the context
   * @return <code>true</code> if another application will be above this one.
   */
  public static boolean isApplicationSentToBackground(final Context context) {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
      ComponentName topActivity = tasks.get(0).topActivity;
      if (!topActivity.getPackageName().equals(context.getPackageName())) {
        return true;
      }
    }

    return false;
  }

For this to work you should include this in your AndroidManifest.xml

<uses-permission android:name="android.permission.GET_TASKS" />
Community
  • 1
  • 1
peceps
  • 17,370
  • 11
  • 72
  • 79
  • Isn't there an event to allow a receiver to do that? – StErMi Mar 27 '12 at 07:29
  • There is an issue here. This doesn't guarantee that the app is in background. The app could still be running and as a result the am.getRunningTasks() could return your app as the running app. in which case this method will return false. From their documentation: Note that "running" does not mean any of the task's code is currently loaded or activity -- the task may have been frozen by the system, so that it can be restarted in its previous state when next brought to the foreground. More here : http://developer.android.com/reference/android/app/ActivityManager.html#getRecentTasks(int, int) – RPM Dec 21 '12 at 22:27
  • I solved the above issue I mentioned by calling the code in onStop() – RPM Dec 22 '12 at 00:24
  • Note, that there are some issues with the suggested solution. See this: http://stackoverflow.com/a/5862048/1037294 – a.ch. Jan 08 '14 at 15:21
  • 1
    The API call `getRunningTasks` is deprecated – Adi Mar 23 '19 at 10:44
  • @Adi this solution is old and should no longer be used. See the link at the top of the answer. – peceps Mar 25 '19 at 12:07
24

This is the method that I used and it seems to work pretty well:

I have a top level Application class of my own that extends Application as such

public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks {

You also need to register this Application object in your manifest file:

<application android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:name=".MyApplication">

Notice how I also implement the ActivityLifeCycleCallbacks interface. This interface has the following methods:

public static interface ActivityLifecycleCallbacks {
    void onActivityCreated(android.app.Activity activity, android.os.Bundle bundle);

    void onActivityStarted(android.app.Activity activity);

    void onActivityResumed(android.app.Activity activity);

    void onActivityPaused(android.app.Activity activity);

    void onActivityStopped(android.app.Activity activity);

    void onActivitySaveInstanceState(android.app.Activity activity, android.os.Bundle bundle);

    void onActivityDestroyed(android.app.Activity activity);
}

You need to implement those methods and then register for these events in your applications onCreate() as follows

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(this);
}

This will then call the callback (the MyApplication object) whenever an activity lifecycle method happens such as onCreate(), onPause etc. In your onActivityPaused() you can then check if the app was backgrounded or not by calling @peceps method : isApplicationSentToBackground(...)

This is what my code looks like then...

/**
 * Application.ActivityLifecycleCallbacks methods
 */
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {

}

@Override
public void onActivityStarted(Activity activity) {
}

@Override
public void onActivityResumed(Activity activity) {
}

@Override
public void onActivityStopped(Activity activity) {
    try {
        boolean foreground = new ForegroundCheckTask().execute(getApplicationContext()).get();
        if(!foreground) {
            //App is in Background - do what you want
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

@Override
public void onActivityPaused(Activity activity) {
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}

@Override
public void onActivityDestroyed(Activity activity) {
}

Create a new class to go the Foreground checking (which is an async. task). See check android application is in foreground or not? for more.

class ForegroundCheckTask extends AsyncTask<Context, Void, Boolean> {
    @Override
    protected Boolean doInBackground(Context... params) {
        final Context context = params[0];
        return isAppOnForeground(context);
    }

    private boolean isAppOnForeground(Context context) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        if (appProcesses == null) {
            return false;
        }
        final String packageName = context.getPackageName();
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
                return true;
            }
        }
        return false;
    }
}
Community
  • 1
  • 1
RPM
  • 3,426
  • 2
  • 27
  • 35
23

Edit

This answer only serves for one purpose, that is, running a code in onPause() for all activities. It doesn't let you run a code when your app is sent to background.

Original Answer

Make an Activity named YourBasicActivity and override its onPause() method and extend every Activity from YourBasicActivity

Adil Soomro
  • 37,609
  • 9
  • 103
  • 153
  • 1
    hmm. this is a good suggestion :) but I have in my app many different activities.. such as ListActivity.. how would I do it then? – Linora Apr 08 '11 at 10:24
  • 1
    In that case you would have to write YourListActivity because it is specialized form of 'Activity'.(In case if you are using so many ListActivity.) – Adil Soomro Apr 08 '11 at 10:29
  • 36
    But onPause() is also called when we switch from one activity to another, not just when the application is closed or sent to background. – peceps Jun 15 '11 at 11:03
  • 1
    @peceps you are rite so which function can i use only when the user closes the app? – user3546693 Jun 26 '15 at 10:26
  • @Herter were you able to solve the problem? If so can you please tell us how? – mohitum Oct 07 '15 at 12:14
  • An extra +1 for @perceps comment. – Bamerza Oct 24 '15 at 20:16
  • Your answer does not 'answer' the question. An answer should not serve "some" purpose, but to answer the question. imho you should remove this answer. – JacksOnF1re Oct 20 '17 at 14:30
  • I think the title is misleading, answer is what OP wants in their question body: "My current solution is to run it on the OnPause() on my home activity, but I need this to run no matter which activity the user is on when closing the app.." – Adil Soomro Oct 20 '17 at 15:49
  • Yes, and this post won't answer the question @Adil. Because it will run, even if the app is not in background, but one activity starts another. Therefore, this answer is just wrong. – JacksOnF1re Oct 21 '17 at 09:47
3

Maybe this can be helpfull, tell me if it worked for you. only when you return from background the value of activities would be 0 (zero) the rest of the times would be a number higher than 0(zero) when the onRestart() is executed.

public class FatherClass extends Activity {

private static int activities = 0;

public void onCreate(Bundle savedInstanceState, String clase) {
    super.onCreate(savedInstanceState);
}

protected void onRestart()
{
    super.onRestart();
    if(activities == 0){
        Log.i("APP","BACK FROM BACKGROUND");
    }
}

protected void onStop(){
    super.onStop();
    activities = activities - 1;
}

protected void onStart(){
    super.onStart();
    activities = activities + 1;
}

}

All of your classes must extend from this class for this to work.

Explanation: The onStart is executed one the activity is "visible" and the onStop when the activity is "not visible". So when your APP (it says APP not activity) goes to background all the activities are "not visible" so they execute the onStop method, so the idea behind this is to ADD ONE each time an activity es started, and SUBTRACT ONE each time an activity es hided, so if the value of the variable "activities" is zero that means that all the activities that where started in some point are now not visible, so when you APP returns from background and executes the onRestart method on the activity in "front" you can check whether comes from background or is just restarting an activity.

Sridhar
  • 11,466
  • 5
  • 39
  • 43
Jorge Aguilar
  • 3,442
  • 30
  • 34
  • It doesn't work correctly if the app can have only 1 main activity. – Dmitry Sep 07 '18 at 06:08
  • @Dmitry what's the problem you are having? this should work for most common problems, is not perfect, but, should work for most people. Very complex apps might need a more complex approach. – Jorge Aguilar Sep 07 '18 at 22:32
  • The problem with the code is that if I change view on `onStop` then I still can see unchanged content of the view when I return to the app. That's strange but that's exactly what happens. If I use `onPause` / `onResume` instead, then the code thinks that the app is in the background when it isn't. – Dmitry Sep 07 '18 at 23:08
  • But because of the problem with `onPause` / `onResume` I can assume that `onStop` / `onStart` can fail after Android update as well. Or it can fail on fast devices, for example. – Dmitry Sep 07 '18 at 23:09
  • @Dmitry sorry for taking this long to reply, sorry i don't understand your scenario, what you mean with only having one main activity? do you mean you do everything through fragments? if that's the case it should work, other thing i don't get is when you say "change view", you are talking about views or what? if you post some code in a pastebin or something i can better help you, a VERY similar approach has been used by some Microsoft apps used by millions, and is working as expected. – Jorge Aguilar Sep 12 '18 at 17:13
1

you can use onAppForegroundStateChange() method which call when app is open and closed.this method is only called when your app comes in foreground/background. onAppForegroundStateChange() method is better then you used onPause() method because onPause method is also called every time when you go to other activity.

you can use this method like that

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        AppForegroundStateManager.getInstance().addListener(this);
    }

    @Override
    public void onAppForegroundStateChange(AppForegroundStateManager.AppForegroundState newState) {
        if (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND == newState) {
            // App just entered the foreground. Do something here!
        } else {
            // App just entered the background. Do something here!
        }
    }
}
  • 1
    Sounds good, but I can't find this method neither the `AppForegroundStateManager` class – Rafa0809 Apr 23 '18 at 23:17
  • this is the source of the code, https://github.com/ytjojo/androidBaseLib/blob/master/androidBaseLib/src/com/kerkr/edu/app/AppForegroundStateManager.java . you should implemet OnAppForegroundStateChangeListener in Application – tabebqena Nov 01 '19 at 12:27
  • and you should invoke `onActiviyVisible(activity)` and `onActiviyNotVisible(activity)` in each activity `onPause()` & `onResume()` – tabebqena Nov 01 '19 at 13:45
0

If you are trying to submit/save data the user input, there are better ways to go about it than doing it when he tries to close the app. There are many ways to close an app. The user could even turn down the phone. So it's hard to take precautions against all of them.

I'd suggest you submit the data everytime the user stops writing, every any number of seconds or when he presses a button for example if your call to the webservice is too slow.

This way it's more safeproof and it's easier to implement.

Antonio Craveiro
  • 121
  • 1
  • 11
0

override the onStop() method of your Home activity and run the code there.

Aman Alam
  • 11,231
  • 7
  • 46
  • 81
0

I think you need to run your own thread which will check whether all running activities in background or destroyed.

MyBasicActivity extends Activity
{
    private static ArrayList<MyBasicActivity> activities=new ArrayList<MyBasicActivities);
    private boolean started;

    public void onCreate()
    {
       activities.add(this);
    } 

    public void onDestroy()
    {
       activities.remove(this);
    } 

    public void onStart()
    {
       this.started=true;
    }

    public void onPause()
    {
       this.started=false;
    }

    public boolean isStarted()
    {
       return started;
    }
}

MyThread implements Runnable
{
    private ArrayList<MyBasicActivity> activities;

    public MyThread(ArrayList<MyBasicActivity> activities) 
    {
        this.activities=activities;
    }

    void run()
    {
          while(!stopped)
          {
              boolean inBackground=true;
              for(MyBasicActivity activity:activities)
              {
                 if(activity.isStarted())
                 {
                      inBackground=false;
                      break;
                 }
              }
              if(inBackground)
                  //run your code here;
              sleep(10000); //10 secs
          }

    }
}
Barmaley
  • 16,638
  • 18
  • 73
  • 146