android - 后台持久服务的Activity不会被kill掉

标签 android android-service android-lifecycle android-service-binding

在 Android 上,我有一个名为 FirstActivityActivity,它启动一个名为 MyServiceService 来执行网络操作在后台。 ActivityService 始终通过调用方法相互通信。

现在,当用户从 FirstActivity 导航到 SecondActivity 时,后台服务应该被终止或重新创建,而是保持 Activity 状态并且传递给 SecondActivity,后者现在将成为与服务通信的对象。

换句话说,只要两个 Activity 之一在运行,Service 就应该运行,并且在用户导航时它不应该停止在两个 Activity 之间。

其中一个 Activity 将始终处于前台,在此期间,服务应该(最好)永远不会被杀死。我认为这应该不是问题,因为这两个 Activity 之一始终处于 Activity 状态,因此 Android 知道该服务很重要,而不是必须终止的服务。

(如果没有办法防止 Android 不时终止和重新创建服务,我将需要一种方法来优雅地恢复服务的完整状态。)

总而言之,Service 的生命周期应该与“合并”的两个 Activity 的生命周期相同。它应该从第一个开始,并且在两个都被销毁之前不要停止。

那么对于该设置和目标,以下代码是否正确?

public class MyService extends Service {

    public class LocalBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
    }

    ...

}

public class FirstActivity extends Activity {

    private MyService mMyService;

    private ServiceConnection mMainServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            MyService mainService = ((LocalBinder) service).getService();
            mMyService = mainService;
            mMyService.setCallback(FirstActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            mMyService = null;
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        startService(new Intent(FirstActivity.this, MyService.class));
    }

    @Override
    protected void onResume() {
        super.onResume();
        bindService(new Intent(FirstActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mMainServiceConnection != null) {
            unbindService(mMainServiceConnection);
        }

        if (mMyService != null) {
            mMyService.setCallback(null);
        }

        if (!isUserMovingToSecondActivity) {
            stopService(new Intent(FirstActivity.this, MyService.class));
        }
    }

    @Override
    public void onBackPressed() {
        stopService(new Intent(FirstActivity.this, MyService.class));
        super.onBackPressed();
    }

    ...

}

public class SecondActivity extends Activity {

    private MyService mMyService;

    private ServiceConnection mMainServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            MyService mainService = ((LocalBinder) service).getService();
            mMyService = mainService;
            mMyService.setCallback(SecondActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            mMyService = null;
        }
    };

    @Override
    protected void onResume() {
        super.onResume();
        bindService(new Intent(SecondActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mMainServiceConnection != null) {
            unbindService(mMainServiceConnection);
        }
    }

    @Override
    protected void onDestroy() {
        ...
        stopService(new Intent(SecondActivity.this, MyService.class));
    }

    ...

}

这是保证 Activity 后台长期服务不会被杀死或重新创建的最佳方式吗?

Context.BIND_AUTO_CREATE 怎么样?在这里设置这个标志是否正确? Context.BIND_ADJUST_WITH_ACTIVITYContext.BIND_WAIVE_PRIORITY 怎么样——我需要这些吗?

最佳答案

(非常感谢@corsair992 的有用指点!)


如果 Activity 总是按该顺序调用(即 FirstActivity 开始 SecondActivity,而不是相反,那么基本上,您应该尝试“绑定(bind)” Service 的生命周期到 FirstActivity 的生命周期。

一般来说(见后面的注意事项),这意味着:

  • FirstActivity.onCreate() 中调用 startService()
  • FirstActivity.onDestroy() 中调用 stopService()
  • onStart()/onStop()方法中调用bindService()/unbindService()两个 Activity(以访问 Binder 对象,并能够对其调用方法)。

以这种方式启动的服务将一直存在,直到 stopService() 被调用并且每个客户端都解除绑定(bind),参见Managing the Lifecycle of a Service :

These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). (...) In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.

和:

When the last client unbinds from the service, the system destroys the service (unless the service was also started by startService()).

使用这个基本策略,只要 FirstActivity 存在,服务就会存在(即它不会被销毁)。然而,重要的一点仍然存在:如果未明确处理的配置更改(例如屏幕旋转)将导致 Activity 自行重启,并且服务将被销毁(因为我们正在调用 stopService ()onDestroy() 中。

为防止这种情况,您可以检查 isChangingConfigurations()在实际停止服务之前(由于 onDestroy() 回调发生是因为这个原因意味着虽然 Activity 的这个特定实例正在被销毁,但之后它将被重新创建。

因此,完整的解决方案应该是这样的:

public class FirstActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startService(new Intent(this, MyService.class));
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() { ... }

    @Override
    protected void onStart() {
        super.onStart();
        bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        unbindService(mServiceConnection);
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        if (!isChangingConfigurations())
            stopService(new Intent(this, MyService.class));

        super.onDestroy();
    }

虽然 SecondActivity 实现onStart()/onStop() 方法(以相同的方式).


关于您的特定实现的一些注意事项:

  • 没有必要覆盖 onBackPressed(),因为如果 Activity 被销毁,将调用必要的生命周期方法(此外,它可以在不按后退按钮的情况下完成,例如如果调用 finish() 就可以了)。
  • onDestroy() 而不是 onPause() 中停止服务使您不必检查 isUserMovingToSecondActivity

关于android - 后台持久服务的Activity不会被kill掉,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27518798/

相关文章:

android - 为什么不能直接访问本地服务?

android - 在销毁时从未调用过服务

android - 在 Android Wear 中打开表盘选择器的 Intent

android - 我需要 "Android unlocked dev phone"来测试我的 android 项目吗?

android - 更改编辑文本的底线颜色

android - START_STICKY 和 START_REDELIVER_INTENT 之间的区别?

Android:使用支持 fragment 管理器时复制 fragment

android - 可以在服务中使用 LiveData 对象吗?

android - 如果已经登录,请正确跳过登录 Activity

java - Android:如何从设置中设置按钮颜色