Android 应用程序关闭/设置为后台时如何执行后台任务?

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

我的 Android 4+ 应用程序已连接到用于每隔几分钟同步一次数据的自定义 Web 服务。为了确保在线数据始终是最新的,我想在应用程序关闭/发送到后台时触发同步。

在 iOS 下这很容易:

如何在 Android 上执行此操作?

第一个问题是,没有什么等同于applicationDidEnterBackground:。到目前为止我发现的所有解决方案都建议使用主要的 Activities onPause() 方法。但是,只要主 Activity 暂停,就会调用此方法,当另一个 Activity 在应用程序中启动时也是如此。 ActivityLifecycleCallbacks 接口中的 onActivityPaused() 方法也是如此。

那么,如何检测app(不仅仅是Activity)关闭或发送到后台?

第二个问题是,我没有找到任何关于如何运行后台任务的信息。所有解决方案都指向简单地启动一个 AsyncTask,但这真的是正确的解决方案吗?

AsyncTask 会启动一个新任务,但是当应用程序 closed/inactive 时,这个任务也会 运行 吗?我该怎么做才能防止应用程序和任务在几秒钟后暂停?我怎样才能确保任务可以在应用程序完全挂起之前完成?

当您的应用关闭或在后台时,以下代码将帮助您post您的内容:

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;
    }

}

我的代码的更多解释是:

即使您的应用程序在后台,服务也会在后台运行,但请记住,它始终在主线程上运行,因此我创建了一个单独的线程来执行您的后台操作。

服务作为粘性服务启动,即使由于任何原因您的服务在后台被破坏,它也会自动重新启动。

可在此处找到更多详细信息: https://developer.android.com/guide/components/services.html

并检查您的应用是否在 foreground/background 中,以下代码将有所帮助:

  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();

快乐编码!!!!

您可以使用服务来实现您想要的目标。如果您没有调用 stopService(..) 或 stopSelf(),即使 activity 组件已被销毁 ,服务也会在后台 保持 运行方法。

在该服务中,您可以进行异步网络调用,最好使用改造,然后您可以使用从 Web 服务获取的最新数据更新本地存储,如 Sqlite DB。

Here是官方的link,你可以使用无界服务来达到你想要的效果。

接受的答案不正确。

不幸的是,作者认为将新的 Runnable 任务发布到 Handler() 会创建一个单独的线程 - "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() 回调总是在主线程上调用,新的 Handler() 附加到当前线程(即 "main")。所以 Runnable 任务被发布在主线程队列的末尾。

要解决此问题并在真正独立的线程中执行 Runnable,您可以使用 AsyncTask 作为最简单的解决方案:

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

在复制粘贴任何内容之前先想想自己...

对于后台操作,您可以使用WorkManager