一个启动并绑定的 Service 何时会被销毁?

60
我正在查看Android服务文档时,注意到两个相互矛盾的点:
管理服务的生命周期中指定了以下内容:
这两条路径并不完全分离。也就是说,您可以绑定到已经使用startService()启动的服务。例如,可以通过使用标识要播放音乐的Intent调用startService()来启动后台音乐服务。稍后,可能当用户想要控制播放器或获取有关当前歌曲的信息时,活动可以通过调用bindService()来绑定到服务。在这种情况下,stopService()或stopSelf()实际上直到所有客户端解除绑定才会停止服务。
但是在管理绑定服务的生命周期中的文档中:
如果你选择实现onStartCommand()回调方法,那么你必须显式地停止服务,因为此时服务被认为已经启动。在这种情况下,服务会一直运行,直到服务使用stopSelf()自行停止或其他组件调用stopService()停止,无论它是否绑定到任何客户端。可能是我理解有误,但我认为这些陈述是相互矛盾的。请问有人能澄清一下吗?
3个回答

100
同意文档可能需要更清晰的说明。他们想表达的是:
  • 如果您调用startService(),则服务将持续运行,直到您调用stopSerivce()(或从服务内调用 stopSelf())
  • 如果您调用bindService(),则服务将持续运行,直到您调用unbindService()
  • 因此,如果您同时调用startService()和bindService(),则服务将持续运行,直到您调用stopService和unbindService()。单独使用任何一个都无法停止服务。
创建了一个非常简单的Activity和Service,并运行了以下连续的start/stop/bind/unbind序列。我观察到这些调用给出了以下结果。
bind-unbind
bindService() caused:
    onCreate()
    onBind()
unbindService() caused:
    onUnbind()
    onDestroy()

开始绑定-解除绑定-停止

startService() caused:
    onCreate()
    onStartCommand()
bindService() caused:
    onBind()
unbindService() caused:
    onUnbind()
stopService() caused:
    onDestroy()

开始绑定 - 停止绑定 - 解除绑定

startService() caused:
    onCreate()
    onStartCommand()
bindService() caused:
    onBind()
stopService() caused:
    -- nothing
unbindService() caused:
    onUnbind()
    onDestroy()

绑定-开始-停止-解绑

bindService() caused:
    onCreate()
    onBind()
startService() caused:
    onStartCommand()
stopService() caused:
    -- nothing -- still running
unbindService() caused:
    onUnbind()
    onDestroy()

bind-start-unbind-stop

bindService() caused:
    onCreate()
    onBind()
startService() caused:
    onStartCommand()
unbindService() caused:
    onUnbind()
stopService() caused:
    onDestroy()
如您所见,在每个同时调用绑定(bind)和启动(start)的情况下,直到解除绑定(unbind)和停止(stop)都被调用时,服务才会一直运行。解除绑定/停止的顺序并不重要。
以下是在我的简单测试应用程序中从不同按钮调用的示例代码:
public void onBindBtnClick(View view) {
    Intent intent = new Intent(MainActivity.this, ExampleService.class);
    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

public void onUnbindBtnClick(View view) {
    if (serviceIsBound) {
        unbindService(serviceConnection);
        serviceIsBound = false;
    }
}

public void onStartBtnClick(View view) {
    Intent intent = new Intent(MainActivity.this, ExampleService.class);
    startService(intent);
}

public void onStopBtnClick(View view) {
    Intent intent = new Intent(MainActivity.this, ExampleService.class);
    exampleService.stopService(intent);
}

13
感谢您对这项研究所付出的努力。 - anz
到目前为止,这是最清晰的解释。它应该在文档中提供。干得好! - harsh_v
1
被文档所困惑:"当客户端完成与服务的交互后,它调用unbindService()解除绑定。当没有客户端绑定到服务时,系统会销毁该服务。" 这个答案澄清了即使是单个已绑定的服务(使用Context.BIND_AUTO_CREATE),在调用unbindService后仍将保持活动状态。只有在调用selfStop后才会被销毁。这与bindService然后startService的流程相同。谢谢! - lannyf

61
实际上,这两段话互相补充(尽管它们的措辞可能会引导错误),并且两者都符合文档中的图像。让我们来看一下:
这两条路径并不完全独立。也就是说,您可以绑定到已经使用startService()启动的服务。例如,可以通过使用Intent标识要播放的音乐调用startService()来启动背景音乐服务。稍后,当用户想要对播放器进行某些控制或获取有关当前歌曲的信息时,可以通过调用bindService()将活动绑定到服务。在这种情况下,stopService() 或 stopSelf()实际上直到所有客户端解除绑定之前才会停止服务。
要点是:如果您启动了一个服务,然后将客户端绑定到它,然后尝试停止它,则在所有客户端解除绑定之前,服务不会停止(销毁)。第二段不是反驳,而是说明了这个语句。
但是,如果您选择实现onStartCommand() 回调方法,则必须显式停止服务,因为此时服务被视为已启动。在这种情况下, 无论是否绑定到任何客户端,服务都会一直运行,直到服务使用stopSelf()或另一个组件调用stopService()为止。
这意味着:启动并绑定的服务即使没有客户端绑定它也会运行,直到显式停止。诚然,措辞可能有点不够清晰。但是文档中给出的生命周期图表显示了这一点(我非常确定我已经在“现实生活”中观察到了这一点,尽管我目前没有直接的示例)。

那么,假设我想解除绑定并停止已启动和绑定的服务,我应该在解除绑定之前还是之后调用stopService?或者这没有关系? - Dielson Sales
你按照任何顺序都可以做到这一点。在所有客户端都解除绑定并停止服务之前,该服务不会被销毁(正如@ChuckKrutsinger的回复中所示)。 - Stephan
2
根据这个混合服务的图表,在所有客户端解除绑定后,onDestroy()会被调用。这是正确的吗?onUnbind()和onDestroy()之间应该有直接联系吗? - Amit0191

0

没错,它能工作。我想用一个示例代码来完成:

我必须制作一个应用程序,其中一个服务由一个活动启动,该活动必须调用服务中的一些方法,即使活动被杀死,服务也必须在后台运行,并且当活动重新启动时,如果服务正在运行,则不必重新启动服务。我希望这可以帮助您,您可以通过日志查看其工作原理。 所以这就是代码:

 public class MyActivity extends Activity{

    private MyService myService;
    private boolean mIsBound = false;

    private ServiceConnection mConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder binder) {
            MyService.MyBinder b = (MyService.MyBinder) binder;
            myService = b.getService();
            mIsBound = true
            //Do something
            // Here you can call : myService.aFonctionInMyService();

        }
        public void onServiceDisconnected(ComponentName className) {
            // Do something
            mIsBound = false;
        }
    }



    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        //Checked if my service is running
        if (!isMyServiceRunning()) {
            //if not, I start it.
            startService(new Intent(this,MyService.class));
        }
    }

    private boolean isMyServiceRunning() {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager
                .getRunningServices(Integer.MAX_VALUE)) {
            if (MyService.class.getName().equals(
                    service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        doBindService();
    }




    //Connection to the Service
    private void doBindService() {
        bindService(new Intent(this,MyService.class), mConnection,
                Context.BIND_AUTO_CREATE);
    }

    // Disconnection from the service
    private void doUnbindService() {
        if (mIsBound) {
            // Detach our existing connection.
            unbindService(mConnection);
        }
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        doUnbindService();
        super.onPause();
    }

}


public class MyService extends Service{


    public static String Tag = "MyService";
    private final IBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub      
        super.onCreate();
        Log.d(Tag, "onCreate()");

    }

    public class MyBinder extends Binder {
        public LocationService getService() {
            return LocationService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        Log.d(Tag, "onBind()");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // TODO Auto-generated method stub
        Log.d(Tag, "onUnBind()");
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.d(Tag,"onStartCommand()");

        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub

        Log.d(Tag, "onDestroy");
        super.onDestroy();
    }

    public void aFonctionInMyService(){
        //Do Something
    }

}

isMyServiceRunning 这个方法运行缓慢,并且将其放在 onCreate() 活动中是不好的实践。 - Simon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接