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

新手上路,请多包涵

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

在 iOS 下这很容易:

  • 在 AppDelegate 中收听 applicationDidEnterBackground:
  • 使用 beginBackgroundTaskWithName: 启动长时间运行的后台任务并避免在任务仍在运行时被挂起

如何在 Android 上执行此操作?

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

那么,如何检测 应用程序(不仅仅是 Activity )已关闭或发送到后台?

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

AsyncTask 将启动一个新任务,但是当应用程序关闭/不活动时,该任务是否也会运行?我该怎么做才能防止应用程序和任务在几秒钟后暂停?我如何确保在应用程序完全暂停之前任务可以完成?

原文由 Andrei Herford 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 691
2 个回答

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

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

快乐编码!!!!

原文由 Anjali 发布,翻译遵循 CC BY-SA 4.0 许可协议

接受的答案不正确。

不幸的是,作者认为将新的 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() 附加到当前线程(“主”)。所以 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;
}

在你复制粘贴任何东西之前想想你自己……

原文由 Sergey Krivenkov 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题