Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute background task when Android app is closed / set to background?

Tags:

My Android 4+ app is connected to a custom web service that is used to sync data every few minutes. To make sure, that the online data is always up to date, I want to trigger the sync when ever the app is closed / send to background.

Under iOS this is quite easy:

  • Listen to applicationDidEnterBackground: in the AppDelegate
  • Use beginBackgroundTaskWithName: to start you long running background task and to avoid being suspended while the task is still running

How to do this on Android?

First problem is, that there is nothing equivalent to applicationDidEnterBackground:. All solution I found so far propose to use the main Activities onPause() method. However this called any time the main Activity is paused, which is also the case when another Activity is started within the app. The is true for the onActivityPaused() method from ActivityLifecycleCallbacks interface.

So, how to detect that the app (not just an Activity) is closed or send to the background?

Second problem is, that I did not find any information on how to run background task. All solutions point to simply start an AsyncTask, but is this really the correct solution?

AsyncTask will start a new task, but will this task also run, when the app is closed/inactive? What do I have to do, to prevent the app and the task from being suspended after a few seconds? How can I make sure, that the task can complete, before the app is completely suspended?

like image 676
Andrei Herford Avatar asked Sep 15 '16 09:09

Andrei Herford


People also ask

What is the current recommended way to handle long running background tasks in Android?

Recommended solutionScheduling deferred work through WorkManager is the best way to handle tasks that don't need to run immediately but which ought to remain scheduled when the app closes or the device restarts.


2 Answers

The following code will help you to post your contents when your app is closed or in the background:

import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.widget.Toast;  public class SendDataService extends Service {     private final LocalBinder mBinder = new LocalBinder();     protected Handler handler;     protected Toast mToast;       public class LocalBinder extends Binder {         public SendDataService getService() {             return SendDataService .this;         }     }      @Override     public IBinder onBind(Intent intent) {         return mBinder;     }      @Override     public void onCreate() {         super.onCreate();      }      @Override     public void onDestroy() {         super.onDestroy();     }      @Override     public int onStartCommand(Intent intent, int flags, int startId) {         handler = new Handler();         handler.post(new Runnable() {             @Override             public void run() {   // write your code to post content on server             }         });         return android.app.Service.START_STICKY;     }  } 

More explanation of my code is:

Service runs in the background even if your application is in the background, but remember, it always runs on the main thread, because of which I created a separate thread to perform your background operations.

Service is started as a sticky one, as even if because of any reason your service got destroyed in the background it will automatically get restarted.

More details can be found here: https://developer.android.com/guide/components/services.html

And to check if your app is in the foreground/background, the following code will help:

  private boolean isAppOnForeground(Context context) {     ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);     List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();     if (appProcesses == null) {       return false;     }     final String packageName = context.getPackageName();     for (RunningAppProcessInfo appProcess : appProcesses) {       if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {         return true;       }     }     return false;   } }  // Use like this: boolean foregroud = new ForegroundCheckTask().execute(context).get(); 

Happy Coding!!!!

like image 58
Anjali Avatar answered Oct 15 '22 14:10

Anjali


The accepted answer is not correct.

Unfortunately, the author believes that posting a new Runnable task to the Handler() creates a separate thread - "I created a separate thread to perform your background operations" :

@Override public int onStartCommand(Intent intent, int flags, int startId) {     handler = new Handler();     handler.post(new Runnable() {         @Override         public void run() {             // write your code to post content on server         }     });     return android.app.Service.START_STICKY; } 

onStartCommand() callback is always called on the main thread, and the new Handler() is attached to the current thread (which is "main"). So the Runnable task is posted at the end of main thread queue.

To fix the issue and execute the Runnable in a really separate thread you can use AsyncTask as the simplest solution:

@Override public int onStartCommand(Intent intent, int flags, int startId) {     AsyncTask.execute(new Runnable() {         @Override         public void run() {             // ...         }     });     return android.app.Service.START_STICKY; } 

Think yourself before you copy-paste anything ...

like image 24
Sergey Krivenkov Avatar answered Oct 15 '22 12:10

Sergey Krivenkov