四大组件之服务:Service 四大组件

Quibbler 2019-10-7 1499

四大组件之服务:Service


        

1、Service

       Android Service是Android四大组件之一,它主要用来执行一些不与用户交互的long-run的操作. 注意Service如非特意指定,它仅是运行于该进程的一部分代码而已,另外Service并不是运行在单独线程中,而是主线程中。所以尽量要避免一些ANR的操作。

       服务是一种应用程序组件,代表应用程序执行长时间运行的操作而不与用户交互或提供功能供其他应用程序使用的愿望。每个服务类<service> 的包中都必须有一个相应的 声明AndroidManifest.xml可以使用Context.startService()和 启动服务 Context.bindService()

       请注意,服务与其他应用程序对象一样,在其托管过程的主线程中运行。这意味着,如果您的服务要执行任何占用大量CPU资源(例如MP3播放)或阻塞(例如网络)的操作,则它应产生自己的线程来执行此工作。有关更多信息,请参见“进程和线程”IntentService是作为一个标准的实施服务的,有它自己的线程在哪里做它安排其工作。

Service分为如下三类

  • Foreground Service:执行一些对于用户来说是可感知的操作,如audio应用使用Foreground Service来播放歌曲。
  • Background Service:执行的操作对用户而言是不可感知的。
  • Bound Service:主要是提供C/S接口,允许组件与Service进行通信,或者是跨进程的通信。



2、使用Service

        继承Service类,自定义Service:

public class ServiceTest extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBinder();
    }
    @Override
    public void onCreate() {
        super.onCreate();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    class MyBinder extends Binder {
    }
}

       四大组件之一的Service,需在AndroidManifest.xml中注册。使用它一定要在AndroidManifest.xml中声明,在AndroidManifest.xml中声明是为了让PackageManagerService能解析出该Service, 并建立对应的数据结构。

<service
        android:name=".ServiceTest"
        android:enabled="true"
        android:exported="true"/>


2.1、startService

        简单的启动Service方式,在Activity中通过Intent和startService()方法 :

Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);

        多次启动只会执行Service的onCreate()方法一次,创建一个实例。每次startService()都会执行Service中的onStartCommand()方法。这里在onStartCommand()中执行一些操作,耗时操作需要开启线程。不难发现,服务已开启就扔到后台执行不管,执行到什么时候也不知道。而且执行完没有调用stopSelf()方法,后台服务会一直存在。

      

        在Activity中调用stopService()方法,停止服务:  

Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);

        跳转在不同的Activity中调用startService(),只会执行一次Service的onCreate()方法,创建一个Service实例。任何一个地方stopService都会销毁Service的实例。


2.2、bindService

        通常使用方法,在Activity中定义全局变量ServiceConnection和Binder:

    //Activity和Service之间建立关联
    private MyService.MyBinder mBinder;
	
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //和Service建立连接之后,在主线程中使用mBinder和Service获取数据,通信
            mBinder = (MyService.MyBinder) iBinder;
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            //
        }
    };

        然后,一般在Activity的onCrea()方法中通过调用bindService()方法,让前台Activity和后台Service建立连接:

Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);

        在Activity中的onDestory()方法中去unBindService():

    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }

        bindService():创建Service实例,并且会调用ServiceConnection类中的onServiceConnected()方法,在该方法中获取到Binder对象,通过该Binder对象,Activity和Service之间进行交互。同时多次调用bindService()只会执行一次onCreate()方法,创建一个Service实例。

        unbindService():Service和Activity之间解绑,销毁Service实例。

        常规生命周期bindService->unbindService:


2.3、unbindService

       unbindService并不会触发Server调用 onServerDisconnected, 相反,当Server端被杀掉或者crash后就会调用 onServerDisconnected 函数通知Client关于Server挂掉了。

        如果有多个Client绑定到Server端,并且不是最后一个Client调用unbindService的话,则不会触发Server的onUnbind和onDestroy, 如果是最后一个Client调用unbindService, 则会调用 onUnbind -> onDestroy该Service。原因正如前面红字所示。


2.4、 启动另一个Activity->bindService()->返回

        当打开一个Activity,在该Activity中bindService(),返回时,由于Activity的销毁,绑定的Service也会被销毁,执行onDestory()方法。


2.5、 bindService()->再启动另一个Activity->bindService()->返回

        先bindService(),在打开启动另一个Activity.B,在另一个Activity.B中bindService(),此时再返回,Activity.B销毁,但是bindService不会销毁。在Activity.B中调用unBindService,也不会调用Service的onDestory(),但是会减少Service的bind连接计数,当减为零的时候会销毁Service。        

        原因:ActivityManagerService.bringDownServiceLocked方法负责销毁服务,无论stopService或unbindService最终都可能会调用该方法。在真正销毁服务前,会检查和该服务绑定的连接信息,如果有设置过BIND_AUTO_CREATE的链接存在,就不进行销毁。

        每次在一个Activity中bindService()时候,都会和这个Activity建立新的连接,ServiceConnection类中的onServiceConnected()方法会执行,但是由于之前已经bindService(),Service实例已存在,不会再次创建。



3、前台服务

        将Service推送到前台可以提高Service的存活率,可以使用前台服务的方法:

    //把当前服务切换到前台,就是在通知栏显示一个通知。
    startForeground(int id, Notification notification)
    
    //停止前台运行,为true清除通知,为false表示不清除通知
    stopForeground(boolean removeNotification)

        这里有几个坑点

        ①调用startForeground方法的第一个参数为通知id,不能为0

    //图省事,直接用0,是不会出现任何通知的。纠结了很长时间
    startForeground(0, builder.build());

        ②ID且最好不要与已经定义的普通Notification一样,与正常的通知同用一个id可以“隐藏”掉这个通知。不过可以神不知鬼不觉的在是Service成为前台服务,而不会发出通知。



4、Service ANR

        Service虽然是运行在后台的服务,但是也是在主线程,要想执行耗时的任务还得放到子线程中去执行,否则会造成ANR,关于ANR详见《ANR:Application Not Responding》

        如果service运行在Foreground,timeout时间是SERVICE_TIMEOUT即20s, 

        如果它运行在Background, timeout时间为SERVICE_BACKGROUND_TIMEOUT为200s.

        那如果Service在timeout时间内处理完了对应的操作,ActivityThread会调用serviceDoneExecuting通知AMS, service已经完成处理运动了。

        serviceDoneExecuting 会在onCreate/onBind/onStartCommand/onUnbind/onDestroy执行后被调用。



不忘初心的阿甘
最新回复 (3)
  • Quibbler 2019-10-7
    2

           除了BIND_AUTO_CREATE还有很多选项,以下列出每个选项说明:

        自动创建服务只要绑定纯在。
        public static final int BIND_AUTO_CREATE = 0x0001;
     
        包含对unbind调用不匹配的调试帮助
        public static final int BIND_DEBUG_UNBIND = 0x0002;
     
        不允许此绑定将目标服务的进程提升到前台调度优先级
        public static final int BIND_NOT_FOREGROUND = 0x0004;
        表示绑定到此服务的客户端应用程序认为该服务比应用程序本身更重要。当设置时,平台将尝试使内存杀手杀死应用程序,然后杀死它绑定到的服务尽管这不能保证是这种情况
        public static final int BIND_ABOVE_CLIENT = 0x0008;
     
        允许承载绑定服务的进程通过其正常的内存管理。 它将更像是一个正在运行的服务,
        允许系统(暂时)清除过程,如果内存低或一些其他的想法可能有,
        并更积极的使它成为候选人被杀死(并重新启动)如果 运行很长时间。
        public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0010;
     
        不影响目标服务的主机进程的调度或内存管理优先级。允许在后台LRU列表上管理服务的进程,就像在后台的常规应用程序进程一样
        public static final int BIND_WAIVE_PRIORITY = 0x0020;
        这个服务比客户端重要,当客户端是前台进程的时候,服务会被提到前台进程的级别。一般一个进程只能被提高到可见进程的级别,即便客户端是个前台进程。
        public static final int BIND_IMPORTANT = 0x0040;
     
        如果是通过Activity来绑定的服务,允许服务的重要级别随着Activity是否可见而改变
        public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;


  • Quibbler 2020-2-15
    3
    • 安卓笔记本
      5
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com