Android四大组件-Service

概念:

android 四大组件之一,提供在后台运行的服务,属于计算型组件。

特点:

在后台运行,无用户界面,生命周期长。

启动方式

startservice:

不与Activity绑定,启动之后会无限期的运行下去,除非遇到内存低情况被回收,需要调用stopService或stopSelf才会停止。

  • **生命周期 :**onCreate(只执行一次)-onStartCommand-服务运行-onDestory()
    • onCreate 只调用一次,onStartCommand可以调用多次(调用的次数是startService的次数),其他方法只能调用一次。
    • onStartCommand必须返回一个整数= 描述系统在杀死服务后如何继续运行。
      • START_STICK:重建服务,调用onStartCommand,但不会传入上次未发送完的intent,而是使用null intent (所以需要检查)。除非还有启动服务的intent未发送完会继续发送。适用于媒体播放器,不需要执行命令但需要一直运行并随时待命。
      • START_NOT_STICK:不会重建服务,除非还存在未发送的intent。但服务不再必需的时候,这个是避免重启服务的最安全的方法。
      • START_REDELIVER_INTENT:重建服务,并且任何未传入的intent的都会被依次送入。 适用于需要立即回复工作的活跃服务,比如下载文件。
  • **操作:**创建一个Service继承自service,在onStartCommand操作。在context中通过intent方式是启动服务。
  • 只能开启或停止服务,无法操作服务。
  • 调用者退出后,服务仍然存在。
bindservice

与Activity绑定,绑定之后在后台运行,除非调用unBindService或绑定的Context被销毁。

  • **生命周期:**onCreate(只执行一次)-onBind-onUnbind-onDestory ,如果先调用了startservice,已经onCreate,也不会再次调用。

  • **操作:**创建一个Binder继承Binder,通过onBind返回Binder对象,在context中通过serviceConnection取到binder对象并调用bindner的方法,bindService中传入ServiceConnection建立连接。

    //在service中自定义Binder
     class MyBinder extends Binder{
            public void startDownload(){
                Log.d(TAG, "startDownload: ");
            }
        }
        
        //在onBind方法中返回Binder
         private MyBinder myBinder = new MyBinder();
         @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG, "onBind: ");
            // TODO: Return the communication channel to the service.
    
            return myBinder;
        }
    
    //在Activity 中创建serviceconnection
      private  ServiceConnection connection = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
               myBinder = (MyService.MyBinder) service;
               myBinder.startDownload();
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
        //绑定服务
           bindService(new Intent(MainActivity.this,MyService.class),connection,BIND_AUTO_CREATE);
    
  • 除了可以开启或停止服务,还可以获得Service对象,对Service进行操作。

  • 调用者退出后Service随着调用者退出而销毁。

服务的销毁方式:
  • 如果是startservice启动的调用stop service就可以销毁,或者在内存极低的情况下,被回收销毁。
  • 如果是通过bindservice启动的服务调用unbindservice 销毁服务;但是同时startservice和bindservice需要unbindservice及再次调用stopservice才会销毁服务,即当service与activity绑定的情况下,service不再绑定且service处于静止状态时。 另外当service的调用者推出时也会销毁服务。
前台服务:

如果需要service一直保持运行状态(service保活),则可以考虑前台service。效果类似于通知。在service的onStartCommand方法中修改.(需要添加FOREGROUND_SERVICE权限)

        //8.0 适配通知栏
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            NotificationChannel channel = new NotificationChannel("service","test", NotificationManager.IMPORTANCE_DEFAULT);
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            assert manager != null;
            manager.createNotificationChannel(channel);
            NotificationCompat.Builder service = new NotificationCompat.Builder(this, "service");
            service.setContentTitle("执行前台服务的通知");
            service.setContentText("执行前台服务的内容");
            service.setSmallIcon(R.mipmap.ic_launcher);
            notification = service.getNotification();
        }else {
            Intent intent = new Intent(this,MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
            Notification.Builder builder = new Notification.Builder(this);
            builder.setContentTitle("执行前台服务的通知");
            builder.setContentText("执行前台服务的内容");
            builder.setSmallIcon(R.mipmap.ic_launcher);
            builder.setContentIntent(pendingIntent);
            notification = builder.getNotification();
        }
		//设置为前台服务:参数1:唯一的通知标识。参数二:通知
        startForeground(1,notification);

service与Thread的区别

两者无联系。虽然都是在后台执行一下耗时的操作,但是service是运行在主线程的,Thread是开启的子线程运行。

远程服务跨进程通信

远程服务的创建

在注册服务的地方添加属性:

android:process=":remote"

远程服务是 运行在另一个进程。此时服务需要与Activity绑定的话需要使用AIDL

同一个工程下使用:

  • 创建一个AIDL文件,在此文件中定义方法,构建项目会自动生成一个接口文件。改文件是IBinder的子类。

  • 在service文件获取该子类,并在onBinder方法返回。(进程S)

      @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG, "onBind: ");
            // TODO: Return the communication channel to the service.
            return mBinder;
        }
        IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
            @Override
            public String toUppercase(String aString) throws RemoteException {
                if (!TextUtils.isEmpty(aString)){
                   return aString.toUpperCase();
                }
                return null;
            }
        };
    
  • 在activity中(进程C)修改serviceconnection

  • private IMyAidlInterface iMyAidlInterface;  //是IBinder的子类 ,AIDL ,mainactivity 和myservice 是不同的进程,此时实现了跨进程通信
      private  ServiceConnection connection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              iMyAidlInterface  =  IMyAidlInterface.Stub.asInterface(service);
              try {
                  String hello_world = iMyAidlInterface.toUppercase("hello world");
                  Log.d(TAG, "onServiceConnected: "+hello_world);
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
      
          @Override
          public void onServiceDisconnected(ComponentName name) {
      
          }
      };
    

    因此实现了跨进程通信。

    不同工程下使用:

    把AIDL文件和Activity的内容移至另一个工程即可。使用隐式跳转。在service中添加过滤器,在

Intent intent = new Intent(定义的action);
bindService(intent, connection, BIND_AUTO_CREATE);

Binder机制

概念:

Binder实现IBinder接口,Android 中实现跨进程通信的机制。

跨进程通信的原因:

为了数据的独立性和安全性,一个进程不能访问另一个进程的数据,即Android的进程是相互独立、隔离的。如果需要读取另一个进程的数据就需要IPC机制。

IPC机制基本原理
  • 进程的空间分为用户空间及系统空间,系统空间是全部进程公用的,用户空间是每个进程私有的,当需要跨进程通信时,进程1通过系统调用,将需要传递的数据复制到系统空间,由系统空间唤醒进程2的接收线程,通过系统调用将数据发送到进程2的用户空间(第二次复制),从而完成跨进程通信。
Binder机制优点:

传统的跨进程(socket)通信缺点:1)复制两次,费时间。 2)接收数据的缓存有接收方提供,但接收方不知道需要提供多大合适。

而Binder机制调用系统函数mmap()内存映射,只需要复制一次即可。

Binder机制原理

利用Binder驱动创建接收缓存区并实现地址映射关系:根据需映射的接收进程信息,实现内核缓存区接收进程用户空间地址同时映射到同1个共享接收缓存区中。

Binder机制模型步骤
  • 向驱动申请SM,驱动同意后成为SM,管理service。
  • Client与Server与SM的通信都是通过Binder驱动,他们不可以直接与SM交互。
    • 注册服务:
      • Server进程向Binder驱动发起注册服务请求。
      • Binder驱动将注册请求发送给service manager进程。
      • service manager进程添加该service进程,即注册服务。
    • 获取服务:
      • client进程传递需要获取的服务名称,向Binder驱动发起获取服务请求。
      • Binder驱动将请求转发给SM。
      • SM查找到client需要的Server对应的服务信息。
      • 通过Binder驱动将上述信息返回给client进程。
    • 使用服务:
      • Binder驱动为实现跨进程做准备(调用系统mmap()函数)实现内存映射。
        • Binder驱动创建一块接收缓存区
        • 实现地址映射关系:通过SM进程里的server信息找到server进程,实现内核缓存区 和 server进程用户空间地址 同时映射到同一接收缓存中。
      • client进程将参数数据发送到server进程:
        • client进程通过系统调用将数据发送到内核缓存区。 (存在内存映射关系,相当于也发送到了server进程的用户空间地址)
        • Binder驱动通知server进程进行解包。
      • server进程根据client进程要求调用目标方法:
        • 收到Binder驱动通知后,server进程从线程池中取出线程,进行数据解包和调用目标方法。
        • 将最终执行结果写入到自己的共享内存中。
      • server进程将目标方法结果返回给client进程:
        • 由于存在内存映射关系,当server将结果写入自己的内存中,Binder驱动通知client进程获取返回结果(没有起用新线程,之前发送数据的线程被挂起)
        • client进程通过系统调用从内核缓存区接收server进程返回的数据。

服务的保活方式

  • 在onStartCommand方法中返回START_STICK,在服务被杀死的时候会重新启动。
  • 把service的优先级(1000)是最高优先级,也可以把服务改为前台服务,在系统内存不足时不会被回收。
  • 使用AIDL跨进程机制双进程保护。
  • 使用JobService
  • 使用自定义广播,在应用退出时(发送广播启动服务)
  • 使用系统广播,比如开机的时候,点击home键的时候启动广播。

IntentService

定义:
  • Intent Service是继承自service并处理异步请求的服务。内部有一个工作线程处理耗时操作。
  • 启动Intent Service 执行完成后会自动停止,不需要调用stopself。
  • 可以多次启动Intent service,每一个耗时操作会以工作队列的方式在intentservice的onHandleIntent回调中执行,并且是依次执行。
  • 内部封装了Handler Thread 和Handler实现的。
使用场景:
  • 一项任务需要几个子任务进行,几个子任务按顺序进行才算完成。如在后台默默进行耗时的上传和下载操作。
问题
  • 启动intent service不需要创建新的线程?

    在onCreate方法里创建 了HandlerThread,这是一个继承自Thread的类,在onCreate中也获取了Looper进行工作。本来有一个线程,无需创建线程。

  • 为什么不建议通过 bindService() 启动 IntentService?

    intent service源码中的onBind方法默认返回null,不会回调到onHanldeIntent方法中,没有使用到intent service的优点,与普通service无区别。

  • 为什么多次启动 IntentService 会顺序执行事件,停止服务后,后续的事件得不到执行?

    内部使用的是handler机制,多次启动intent service不会重新创建新的线程和服务,而是把消息加到消息队列里,消息队列是一个单链表,消息入列时的操作是根据时间入列,所以会按顺序执行。停止服务后,会将消息队列的消息清空,因此后续的事件得不到执行。

猜你喜欢

转载自blog.csdn.net/MarinaTsang/article/details/84709468