我的Android 4+应用程序已连接到自定义Web服务,该服务每隔几分钟用于同步一次数据。为了确保在线数据始终是最新的,我希望在应用关闭/发送到后台时触发同步。
在iOS下这很容易:
applicationDidEnterBackground:
beginBackgroundTaskWithName:
启动长时间运行的后台任务,并避免在任务仍在运行时被暂停如何在Android上执行此操作?
第一个问题是,没有任何内容等同于applicationDidEnterBackground:
。到目前为止,我发现的所有解决方案都建议使用主要的onPause()
方法。但是,这会在主Activity
暂停时调用,在应用程序中启动另一个Activity
的情况也是如此。对于来自onActivityPaused()
接口的ActivityLifecycleCallbacks
方法,情况属实。
那么,如何检测应用(不仅仅是Activity
)已关闭或发送到后台?
第二个问题是,我没有找到有关如何运行后台任务的任何信息。所有解决方案都指向简单地启动AsyncTask
,但这真的是正确的解决方案吗?
AsyncTask
将启动一项新任务,但当应用关闭/停用时,此任务是否也会运行?我需要做什么,以防止应用程序和任务在几秒钟后被暂停?在应用程序完全暂停之前,如何确保任务可以完成?
答案 0 :(得分:17)
以下代码可以帮助您在应用关闭或在后台发布您的内容:
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
要检查您的应用是否在前台/背景下面的代码会有所帮助:
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();
快乐编码!!!!
答案 1 :(得分:3)
您可以将服务用于您想要实现的目标。如果您没有调用stopService(..)或stopSelf()方法,即使活动组件已被销毁,服务也将继续在后台运行。
在服务中,您可以进行异步网络呼叫,最好使用改造,然后您可以使用从Web服务获取的最新数据更新Sqlite DB等本地存储。
Here是官方链接,您可以使用无限制服务来实现您想要达到的目标。
答案 2 :(得分:2)
接受的答案不正确。
不幸的是,作者认为将新的Runnable任务发布到Handler()会创建一个单独的线程-“我创建了一个单独的线程来执行您的后台操作” :
@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;
}
在复制粘贴任何内容之前都要三思而后行...
答案 3 :(得分:-1)
对于后台操作,您可以使用WorkManager
。