Service

作者: Doctor_Xu | 来源:发表于2020-07-13 23:32 被阅读0次

    简介

    1. Service是一个应用程序组件,可以执行比较耗时的操作而不与用户交互(源码给的介绍是longer-running,它运行于主线程,也是不能执行比较耗时的任务),也可以提供服务供其他程序使用。Service作为四大组件,必须在AndroidManifest.xml文件中进行注册。启动Service有两种方法:startService()和bindService()。
    2. Service像其他应用程序对象一样,也是运行在其宿主进行的主线程。这就意味着,如果Service需要执行高CPU负载(例如播放MP3)或执行阻塞任务(例如网络操作),则需要使用线程执行其任务,或是使用IntentService,IntentService
    3. Service并不是一个单独的进程,Service的运行必须有宿主进程。除非特别指定,否则它与它所属的应用程序在同一进程中执行。同时Service也不是一个线程,它也运行于主线程,因此不能执行耗时任务,以防止出现ANR错误。
    4. 使用startService()启动服务主要是本进程启动本进程代码封装的Service,这个Service会一直执行,直到显示地stop这个Service;使用bindService()启动一个服务主要是用于调用远程服务,可以与远程服务保持长连接以便于远程服务可以与客户端进行交互。
    5. 调用startService()启动Service时,如果之前Service没有启动,则系统会调用被启动Service的onCreate()和onStartCommand()函数,如果Service之前已经被启动,则只会调用onStartCommand()函数,随后Service进行运行状态。直到调用stopService()或是执行stopSelf()函数,则Service停止运行。如果对某一个Service进行多次startService()函数调用,则它的onStartCommand()函数也会被调用多次,但是无论调用多少次启动Service的函数,只要调用一次stopService()或是一次stopSelf()函数,Service即可停止运行。然而Service也可以使用它的stopSelf(int)函数来保证在started intent没有被处理完之后就结束掉自己。
    6. onStartCommand()函数是有返回值函数,它的返回值决定了Service被kill掉之后,如何恢复执行,它的返回值有START_STICKY, START_NOT_STICKY, START_REDELIVER_INTENT。
    7. 客户端也可以使用bindService()函数来启动一个Service,并与Service保持连接。通过bindService()启动Service时,如果它之前没有被启动,也同样会调用它的onCreate()函数,但是不会调用onStartCommand()函数,此时客户端会收到一个它启动的Service通过onBind()函数返回的IBinder对象,通过这个IBinder对象,Client即可以与Service进行交互。当客户端与服务端的连接建立以后,Service会一直保持运行状态(无论客户端是否持有Service的IBinder引用,例如onBind()函数返回null)。
    8. 在执行Service的onDestroy()函数时,一定要对资源进行释放,例如结束Service启动的线程,解注册广播等
    9. 只要Service一被启动或是被执行onBind,Android系统就尝试让应用程序进行托管被启动的Service,也可以理解为:当托管Service的进程的优先级高,Service的优先级也会高,当托管Service的进程的优先级低,Service的优先级也会低。当系统内存不够且需要杀死某些进程时,系统会根据应用程序进程的优先级来决定杀死哪些Service。
    10. 如果Service正在执行onCreate()或是onStartCommand()或是onDestroy()函数,那么它的宿主进程将作为一个前台程序,来保证上面三个函数中的代码执行完毕,而不会被系统杀死。
    11. 如果Service已经启动,则系统认为它的优先级不如当前正在屏幕上运行的可见的进程的优先级高(Android系统认为处于前台的且与用户交互的进程优先级最高),但是比其他运行于后台的用户不可见的进行优先级高。因为一般情况下,只有有限的几个进程对于用户来说是可见的(可参照分屏等业务),这就意味着除非系统处于低内存状态,否则系统不应该强制结束Service。然而,用户不会时刻关注着后台服务的运行状态(一般情况下服务与用户没有交互),但是系统在低内存状态下杀死Service是为了回收内存资源,可是被杀死的Service之前一直是处于有效的运行状态,因此系统回收到了足够的内存后,还会启动相应被杀死的Service,作为开发者来说,应该注意到系统这个机制的存在。尤其是长时间运行的Service会增加其被系统杀掉的风险,如果Service启动的时间足够长,则它一定会被系统杀死的(但是在适当的情况下系统会重新启动此Service)
    12. 如果客户端通过onBind()函数绑定Service,那么Service的宿主进程一定是比任何一个客户端都重要。那就是说:如果其中一个客户端对用户可见,基于前面说的Service的重要性(这可以理解为优先级)比任何一下客户端的重要性都高,那么这个Service也被认为是用户可见的。可以通过Context的BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, BIND_WAIVE_PRIORITY等参数来实现客户端的重要性影响Service的重要性。
    13. 可以使用startForeground(int, Notification) API把一个已经启动的Service设置为前台Service,通过这种方式来提高Service的优先级,使用这种方式,在系统低内存状态下也不会杀死此Service,(但是在极端情况下,内存极度不够用等状态,系统有可能也会杀死此Service,但是在实际使用过程中,可以不用考虑这个情况)。
    14. 基于上面介绍的在系统有比较大的内存压力时,会杀死Service,如果Service被杀死后,系统会在稍后的某一时刻尝试重新启动被杀死的Service,基于系统会重新启动被杀死的Service,如果是调用onStartCommand()函数来启动Service且执行异步任务或是启动其他线程执行任务,那么调用onStartCommand()函数时,应该返回START_FLAG_REDELIVERY以保证系统重启Service时,把之前用户调用时传递的Intent对象重新传递给Service,如果不使用START_FLAG_REDELIVERY返回值,则系统重新启动Service时,传递给onStartCommand()的intent参数是null。
    15. Service最常用的使用场景是作为应用程序的组件执行,与运行于应用程序中的其他组件一样,是应用程序的一部分。所有的APP程序的组件都运行在同一个进程中,除非明确指定程序是多进程执行。
    16. 如果Service和Client要做复杂的交互,使用Messenger比使用AIDL更方便。如果需要让Service运行于其他进程,可以在AndroidManifest.xml文件中指定其android:prcess属性。

    API

    /**
         * Called by the system when the service is first created.  Do not call this method directly.
         * 当启动或是Bind服务时,系统自动调用此函数,用户可以重写此函数做一些自定义的初始化工作
         * startService()和bindService()时,只要Service未启动,则都会调用此函数
         */
        public void onCreate() {
        }
    
    /**
         * 每次调用startService()函数时,系统都会调用onStartCommand()函数,注意和onCreate()函数的调用次数进行区分 
         * 这个函数默认有返回值,返回值决定了系统杀掉Service后,重启此Service时的处理方式
         *
         * 这个函数接受两个参数,一个是Intent对象,一个是startId
         * 如果在Service的宿主进程退出后,这个Service被重启,这个Intent对象有可能是null,
         * 除非onStartCommand()函数的返回值是START_STICKY_COMPATIBILITY
         * 
         * startId参数用于唯一标识所启动的Service,当调用stopSelfResult()函数时,也需要使用此startId
         */
    public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
            onStart(intent, startId);
            return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
        }
    
    onStartCommand()函数几个返回值的意义
    
    /**
         * 当Service被系统杀死后,系统重新启动此Service时,会继续调用onStartCommand()函数
         * 而此时向onStartCommand()函数中传递的Intent参数是null
    */
    public static final int START_STICKY = 1;
    
    /**
         * 当Service被系统杀死后,系统不会重新启动此Service,
         * 也就是说:Service由于某种原因(系统内存压力等)被系统杀死后,系统并不会重新启动它,
         * 除非有Client通过startService()函数启动此Service
    */
    public static final int START_NOT_STICKY = 2;
    
    /**
         * 当Service被系统杀死后,系统重新启动此Service时,会继续调用onStartCommand()函数
         * 而此时系统会把之前启动此Service时传递的Intent对象传递给onStartCommand()函数
         * 返回此数值时,系统会保存Intent对象,以便在重启此Service时向它传递此对象
    */
    public static final int START_REDELIVER_INTENT = 3;
    
    /**
         * 结束Service时,由系统调用此函数,此时Service应该做一些清理化的工作,
         * 例如清理掉所占用的资源,结束掉所启动的线程等等
         *
         * 在这个函数返回之前,此Service将不会接受任何其他函数调用,
         * 例如在onDestroy()函数执行过程中,别的线程调用它的startService()函数,此时这些函数调用都是不起作用的
         * 也就是说,在执行onDestroy()函数时,这个Service就已经Dead
         */
        public void onDestroy() {
        }
    
    /**
         * 返回此Service的一个IBinder对象,客户端通过此对象和Service进行交互
         * 如果Client和Service没有进行Bind操作,则此返回值是null
         *
         * 不像其他应用程序组件,调用onBind()接口返回时并不一定返回此进程的主线程
         */
        @Nullable
        public abstract IBinder onBind(Intent intent);
    
    /**
         * 当此Service的所有Client都与此Service断开连接后,才会调用此函数。
         * 此函数的默认返回值是false
         * 返回值为false:标识当有新的客户端重新bind到此Service后,不调用onRebind()函数
         * 返回值为true:标识当有新的客户端重新bind到此Service后,调用onRebind()函数
    */
    public boolean onUnbind(Intent intent) {
            return false;
        }
    
    /**
         * 此函数是否调用,受上面onUnbind()函数返回值的影响
    */
    public void onRebind(Intent intent) {
        }
    
    /**
         * 结束Service,传递的参数如果是最近一次启动Service的startId,则Service会立即停止
         * 如果不是最近一次启动Service的startId,则Service不会停止
         * 主要是为了让Service能够及时处理完之前的任务
         * 例如startId的序列是1,2,3,4,5
         * 如果stopSelfResult(3),由于它的id并不是最新的5,所以Service并不会结束,且此函数返回值为false
         * 如果stopSelfResult(5),由于它的id是最新的id = 5,所以Service会马上结束,并且此函数的返回值为true
    */
    public final boolean stopSelfResult(int startId) {
            if (mActivityManager == null) {
                return false;
            }
            try {
                return mActivityManager.stopServiceToken(
                        new ComponentName(this, mClassName), mToken, startId);
            } catch (RemoteException ex) {
            }
            return false;
        }
    
    /**
         * 设置前台Service,主要是有UI提示效率,且优先级比较高,不容易被系统杀死
         * 常用于音乐播放等,当被结束之后,可以有UI提示效果
    */
    public final void startForeground(int id, Notification notification) {
            try {
                mActivityManager.setServiceForeground(
                        new ComponentName(this, mClassName), mToken, id,
                        notification, 0, FOREGROUND_SERVICE_TYPE_MANIFEST);
            } catch (RemoteException ex) {
            }
        }
    
    /**
         * 结束前台Service,注意这个并不是Kill掉Service,只是删除其前台Service的属性
         * 执行了此函数后,Service依然会保持运行状态,只是会删除其前台属性且在系统低内存时可以杀死此Service
    */
    public final void stopForeground(@StopForegroundFlags int flags) {
            try {
                mActivityManager.setServiceForeground(
                        new ComponentName(this, mClassName), mToken, 0, null,
                        flags, 0);
            } catch (RemoteException ex) {
            }
        }
    

    相关文章

      网友评论

          本文标题:Service

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