启动和绑定的服务何时销毁?

Posted

技术标签:

【中文标题】启动和绑定的服务何时销毁?【英文标题】:When is a started and bound Service destroyed? 【发布时间】:2013-06-13 08:46:40 【问题描述】:

当我注意到两个矛盾点时,我正在浏览 android 中的服务文档:

在服务文档中,它在Managing the Lifecycle of a Service中指定

这两条路径并不完全分开。也就是说,您可以绑定到 已经使用 startService() 启动的服务。例如,一个 背景音乐服务可以通过调用 startService() 来启动 带有标识要播放的音乐的 Intent。以后,可能什么时候 用户想要对播放器进行一些控制或获取 关于当前歌曲的信息,一个活动可以绑定到 通过调用 bindService() 提供服务。在这种情况下,stopService() 或 stopSelf() 直到所有客户端才真正停止服务 解绑。

但是在Managing the Lifecycle of a Bound Service中关于绑定服务的文档中

但是,如果您选择实现 onStartCommand() 回调 方法,那么您必须显式停止该服务,因为该服务 现在考虑启动。在这种情况下,服务会一直运行到 服务通过 stopSelf() 或其他组件调用自行停止 stopService(),不管它是否绑定到任何客户端。

可能是我,但我认为这些陈述是矛盾的。谁能澄清一下...

【问题讨论】:

【参考方案1】:

实际上,这两段是相辅相成的(尽管它们的措辞可能会产生误导),并且两段都与文档中的图像一致。一起来看看吧:

这两条路径并不完全分开。也就是说,您可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用标识要播放的音乐的 Intent 调用 startService() 来启动背景音乐服务。稍后,可能当用户想要对播放器进行一些控制或获取有关当前歌曲的信息时,活动可以通过调用 bindService() 绑定到服务。在这种情况下,stopService() 或 stopSelf() 在所有客户端解除绑定之前实际上不会停止服务。

精髓是:如果你启动一个服务,然后绑定一个客户端到它,然后尝试停止它,在所有客户端解除绑定之前服务不会停止(销毁)。第二段并不矛盾,它细化了这一说法。

但是,如果您选择实现 onStartCommand() 回调方法,那么您必须显式停止服务,因为现在认为服务已启动。在这种情况下,服务会一直运行,直到服务使用 stopSelf() 或另一个组件调用 stopService() 自行停止,无论它是否绑定到任何客户端。

这意味着:即使没有客户端绑定到它,启动和绑定的服务也会运行,直到它被显式停止。诚然,这方面的措辞可能更清楚一些。然而,文档中给出的生命周期图显示了这一点(我很确定我已经在“现实生活”中观察到了这一点,尽管我目前没有直接的例子):

【讨论】:

那么,假设我想取消绑定和停止一个已经启动和绑定的服务,我应该在取消绑定之前调用 stopService 吗?还是没关系? 按什么顺序执行并不重要。在并非所有客户端都未绑定并且已停止之前,该服务不会被销毁(正如您在 @ChuckKrutsinger 的回复中看到的那样)。 根据这个图在一个混合服务中,在所有客户端Unbind onDestroy()被调用之后,正确吗?onUnbind()和onDestroy()之间应该有直接联系吗?【参考方案2】:

同意文档可以更清晰。他们想说的是:

如果您调用 startService(),则服务将继续运行,除非您调用 stopSerivce()(或从服务内调用 stopSelf()) 如果您调用 bindService(),则服务将继续运行,除非您调用 unbindService() 因此,如果您同时调用 startService() 和 bindService(),则服务将继续运行,直到您同时调用 stopService 和 unbindService()。两者都不会自行停止服务。

创建了一个非常简单的活动和服务并运行以下启动/停止/绑定/取消绑定序列。我观察到这些调用给出了以下结果。

绑定-解除绑定

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

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

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

【讨论】:

感谢您对此的研究 目前为止最清晰的解释。这应该在文档中可用。干得好! 被文档弄糊涂了:“当客户端完成与服务的交互时,它会调用 unbindService() 来解除绑定。当没有客户端绑定到服务时,系统会销毁服务。” .这个答案清楚地表明,在调用 unbindService 后,即使是单个有界服务(使用 Context.BIND_AUTO_CREATE)也仍然存在。它只会在 selfStop 被调用后被销毁。这与 bindService 然后 startService 流程相同。谢谢!【参考方案3】:

是的,它有效。 我想用一个示例代码来完成:

我必须制作一个由活动启动的服务的应用程序,活动必须调用服务中的一些方法,即使活动被杀死,服务也必须在后台运行,并且当活动重新启动时,它没有如果它正在运行,则不要重新启动服务。我希望它会对你有所帮助,你可以看看它是如何与 Log 一起工作的。 这就是代码:

 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() 活动中是不好的做法

以上是关于启动和绑定的服务何时销毁?的主要内容,如果未能解决你的问题,请参考以下文章

销毁应用程序时停止服务

无论如何要知道何时销毁池线程(或 ThreadStatic 成员)?

java中静态成员变量、实例变量、局部变量何时创建、何时销毁?

当Activity被销毁时,为什么Service会破坏自我?

用bind的方法启动service,调用者退出后,service也销毁?

在 JSF 中如何以及何时销毁 @ViewScoped bean?