Android-Service

作者: 最黑暗的自己 | 来源:发表于2020-01-27 20:50 被阅读0次

    服务概括

    服务是一种可在后台长期运行不需要界面的应用组件,它可以由其他组件启动,且不依赖于其他组件,此外服务可以通过绑定与其他组件进行交互,甚至执行进程间通信(IPC)。最常见的运用服务的app如音乐播放器可在后台播放音乐,不依赖于activity界面。

    服务类型

    1、前台:前台服务执行的是用户能注意的操作,需要显示通知。
    2、后台:后台服务执行的是用户不会注意的操作,与前台服务相反。
    3、绑定:绑定服务会提供客户端-服务器接口,组件与服务交互以及进程间通信都是通过绑定服务,注意:多个组件可同时绑定到一个服务,当所有绑定解绑后,服务才会被销毁。

    服务的启动方式

    启动service的方法有两种:startService和BindService,两种方法涉及的生命周期大致相同,具体如下图所示:
    以下说明各个生命周期含义:

    1. onCreate:创建服务
    2. onBind:绑定服务
    3. onStart:(已过时)
    4. onStartCommand:启动服务,当调用startService时会调用该方法
    5. onUnbind:解绑服务
    6. onPause:暂停服务
    7. onDestroy:销毁服务
      以上参考android官方文档地址:https://developer.android.google.cn/training/best-background

    通常启动服务往往伴随着绑定服务,所以其中的生命周期复杂多样,往往启动跟绑定交叉使用,以下重点梳理下不同方式所执行的生命周期:
    [图片上传失败...(image-2a58f5-1580129516189)]
    以上是两种简单的启动停止Service方式,左图为startService->stopService,右图为bindService->unbindService。
    往往启动跟绑定是交叉使用,如下图是交叉使用的生命周期图,一般启动服务所涉及的生命周期如图箭头所示。


    android_service.png

    根据上述流程图,有几点需要注意:
    1、startService后执行bindService如果使用的是BIND_AUTO_CREATE参数,接下来使用stopService服务并不会停止或者解绑。
    2、使用bindService(BIND_NOT_FOREGROUND)绑定服务后,服务并不会启动,只是单纯的绑定了服务。
    3、使用bindService(BIND_AUTO_CREATE)绑定服务后,服务可以启动并绑定,如果调用stopService服务并不会解绑或销毁。
    在调用bindService时需要传入flags参数,列举通常使用的几个参数:
    1、BIND_AUTO_CREATE:只要绑定服务,就自动创建服务
    2、BIND_NOT_FOREGROUND:系统将阻止驻留该服务的进程具有前台优先级,仅在后台运行。

    ServiceConnectionLeaked

    该问题在使用service时常常出现,具体报错提示如下图


    error.png

    出现该问题的场景是:如果在activity finish前没有解绑service就会出现这个error。
    解决方法是在activity 销毁前unbindService,建议在onPause/onDestroy时解绑服务,具体在哪个方法视场景而定。
    这个error是怎么产生的呢?按字面理解应该是类似于内存泄露,activity销毁了但是ServiceConnection还没有解绑导致。
    如下图报错提示:


    serviceConnectionLeaked.png
    根据报错提示,总结以下流程图
    ServiceConnectionLeakedAnalyse.png

    可以看出该报错是从ActivityThread.java中handleDestroyActivity导致,handleDestroyActivity方法是activity销毁的回调方法,那是什么情况导致这个error抛出呢?从removeContextRegistrations方法中看到当ArrayMap mServices中key为context的ArrayMap 不为空时就会抛出该错误,我们在bindService时,会将ServiceConnection用ArrayMap保存起来,如果activity销毁时没有解绑,就会进入该error。

    开发过程中对于这个问题又出现了一种特殊情况,在startService->bindService(BIND_NOT_FOREGROUND)->stopService后,生命周期虽然在执行stopService后onUnbind->onDestroy,但是此时只要一销毁activity还是会报这个错误。如果是unbindService在ContextImpl类中会 执行mPackageInfo.forgetServiceDispatcher(),将ArrayMap中的ServiceConnection移除,但是如果是 stopService并不会操作ArrayMap,但是为什么stopService还是会出发onUnbind呢?
    根据context.stopService进入到ActivityManagerService,ActivityServices,stopService最终执行bringDownServiceLocked,在该方法中会执行以下4个操作:
    1、关闭所有客户端连接,通过IPC触发客户端的ServiceConnection.onServiceDisconnected
    2、清理资源(ArrayList)
    3、通过IPC触发服务的onUnbind生命周期方法
    4、通过IPC触发服务的onDestory生命周期方法
    所以这就可以说明为什么stopService会触发onUnbind生命周期了。

    附上stopService时序图


    stopService时序图.png

    IntentService

    service先研究到这里,通常后台服务常伴随着耗时操作,service通常在其托管进程的主线程中运行,它既不创建自己的线程,也不在单独的进程中运行,除非你在其中创建新的子线程,所以android提供了一个新的类IntentService,它继承Service,根据其生命周期onCreate->onHandleIntent->onDestroy,onHandleIntent方法会在子线程中执行,并且是处理完自动停止,并且一次只能处理一个任务,由于IntentService继承Service,所以也支持绑定、解绑。如下是IntentService源码中onCreate所执行的代码,可以看出在onCreate中创建了一个工作线程HandlerThread,使用ServiceHandler来作为消息执行者,这里用到了HandlerThread+Handler组合带消息循环机制的异步任务处理机制。

     @Override
        public void onCreate() {
            // TODO: It would be nice to have an option to hold a partial wakelock
            // during processing, and to have a static startService(Context, Intent)
            // method that would launch the service & hand off a wakelock.
    
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    

    备注补充知识点:
    1、进程间通信(IPC)
    2、activity管理-ActivityClientRecord
    3、HandlerThread+Handler组合带消息循环机制的异步任务处理机制
    4、整理生命周期时序图完成stopService(service)
    5、ScrollView嵌套RecyclerView显示不完全。

    相关文章

      网友评论

        本文标题:Android-Service

        本文链接:https://www.haomeiwen.com/subject/gmwethtx.html