二 Service总结

只是写一遍加深印象。

1 概述

一种后台服务。使用时需要在Manifest声明。
两种使用方式:

  1. 启动模式。适用于Service不需要与启动组件交互的场景。
  2. 绑定模式。适用于Service需要与启动组件交互的场景,当解除此类Service的所有绑定者后,Service也被销毁。

2 声明

使用Service前需要在Manifest中声明,其属性有:

  1. exported[true|false]。是否支持隐式调用。
  2. name
  3. permission
  4. process[:xxx|xxx]注意这里的冒号,有冒号代表进程名为“包名:xxx”,无冒号代表进程名为不加包名前缀的“xxx”。
  5. isolatedProcess[true|false]。进程与其他进程分开且无权限,通信只能通过Service的API(bindstart)。
  6. enable。父标签(若有)和子标签必须同时为true,该服务才有效。

3 启动服务

3.1 startService启动

这种方式启动的Service不会随着启动者的退出而停止,可以用stopService(Intent)或者服务自身调用stopSelf停止。
每次启动都会调用onStartCommand

需要关注的方法

  • onCreate
    首次创建Service时回调此方法,之后不会再回调。

  • onStartCommand(Intent intent,int flag,int startId)
    startService时会回调,然后启动服务。

    • @params
      intent是启动服务时的传参。
      flag有三个值:
      1.0,无参数。
      2.START_FLAG_REDELIVERY。代表本方法的返回值为 START_REDELIVER_INTENT,当service因内存不足被系统关闭后,会 用最后一个传入的intent作为参数再次调用本方法(onStartCommand)。
      3.代表当本方法调用后一直没有返回值时,会重新调用本方法。
      startId当前服务id。
    • @return
      三个可选值:
      1.START_STICKY。Service因内存不足被杀死后,尝试重新创建此服务并回调onStartCommand,传入intent为空(除非有挂起的pendingIntent)。适用于无限期运行等待作业的播放器等。
      2.START_NOT_STICKY。不重启。
      3.START_REDELIVER_INTENT。重启并传递最后的intent。适用于恢复下载等。
  • onDestroy
    在这里清理所有资源。

3.2 bindService启动

Service处于绑定状态时,相当于“client-server”模型中的server,client通过代理与server交互。
绑定的Service的生命周期与宿主绑定,宿主退出即服务销毁。
显然,在绑定服务时必须提供IBinder接口的实现。

定义Service接口的三种方法

1.扩展Binder类。
client通过binder调用service提供的能力。常用于service作为本应用的后台线程的情况,比如下载、播放等。
唯一不适用的场景是service需要提供给其他app调用。
2.使用Messenger
Messenger用于跨进程传递数据(基于Handler、AIDL,本质是封装之后的AIDL),可以使用其为服务定制接口。
Messenger串行处理客户端发来的消息(线程安全)。
3.使用AIDL
允许同时处理多个跨进程的请求。
一般同时采用线程安全式设计。使用时需要自定义.aidl文件。SDK工具利用该文件生成实现接口并处理IPC的抽象类,随后对其进行扩展。

3.2.1 扩展Binder类

1.在Service的子类(目标业务类)中创建一个实现Binder接口的实例对象,并实现公共方法用于客户端调用
2.onBind()回调返回此Binder实例;
3.在客户端中,从onServiceConnected()回调方法接收Binder,并用提供的方法调用服务的能力。

典型场景是后台音乐服务。服务和客户端必须在同一进程内。

// flag 0代表不创建service,BIND_AUTO_CREATE代表绑定时自动创建service
bindService(intent,conn,Service.BIND_AUTO_CREATE);



conn = new ServiceConnection() {
    /**
     * @param name 封装组件的描述信息,很少使用
     * 与服务器端交互的接口方法 绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象,
     * 通过这个IBinder对象,实现宿主和Service的交互。
     */
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "绑定成功调用:onServiceConnected");
        // 获取Binder
        // 注意这里是将IBinder接口强转为Binder实现类
        LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
        mService = binder.getService();
    }
    /**
     * 当取消绑定的时候被回调。但正常情况下是不被调用的,它的调用时机是当Service服务被意外销毁时,
     * 例如内存的资源不足时这个方法才被自动调用。
     * 当客户端取消绑定,不会回调此方法
     */
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mService=null;
    }
};

多次调用bindServiceunbindService,对应的onBindonUnbind也只会回调一次。
绑定方式的service生命周期为onCreate->onBind->onUnbind->onDestroy

3.2.2 Messenger

用于不同进程间的通信。如果需要互相发送消息,需要在服务端和客户端都布置Messenger和Handler。大致过程如下:
1.服务端实现Handler,用于接收客户端的回调;
2.使用上述Handler创建Messenger
3.用Messenger创建IBinder,在serviceonBind方法中,将其返回客户端;return mMessenger.getBinder();
4.客户端收到IBinder后,用其实例化Messengernew Message(iBinder)),用其发送Message
5.MessengerHandler中(handleMessage())接收Message并处理。

二 Service总结_第1张图片
messenger.jpg

3.2.3 绑定服务注意

1.多个客户端可以同时绑定一个服务,只有首次绑定回调onBind,其余情况系统会自动传递IBinder;除非startService也启动了该服务,否则最后一个客户端取消绑定时,服务被销毁。
2.应该在客户端的引入和退出时刻绑定、注销服务。只需要在activity可见时存在的服务应绑定在onStartonStop;需要在activity存活期间都运行的服务,应该绑定在onCreateonDestroy,并且应该提高进程的权重。
3.不应该在onPauseonResume期间绑定、取消绑定,它们太频繁,并且可能引起服务的销毁和重建
4.远程方法可能引起DeadObjectException,由连接中断引发,表示调用的对象已死亡(service对象已销毁),这是远程方法的唯一异常,继承RemoteException
5.bindService方法内部,系统会调用服务的onBind方法,返回用于与服务交互的IBinder该方法是异步的

3.2.4 启动服务、绑定服务的优先级问题

startService(启动服务)的优先级大于bindService(绑定服务)。若同时启动、绑定了服务,在调用stopService或者stopSelf之前,即使所有客户端都取消绑定了,服务在完成前也不会停止。
同样,在仍有客户端绑定在服务上时,stopService或者stopSelf也不会停止服务。

3.3 服务在其托管进程的主线程运行

如果需要进行耗时操作,需要另起新的线程。

3.4 前台服务以及通知

前台服务在状态栏提供无法清除通知(除非服务停止或者从前台删除),例如音乐播放器。
两个相关的方法:
1.startForeground(int id,Notification)。参数显然为标识id(不得为0)和通知。
2.stopForeground(boolean removeNotification)
从前台删除服务并删除通知。

3.5 常见使用场景

下载文件,service后台执行,notification前台显示,thread异步下载。
app维持service从网络获取推送服务。
这里引入讨论IntentService

3.6 生命周期

二 Service总结_第2张图片
service_life.jpg

3.7 显示、隐式启动

  • 显示启动
Intent  intent=new Intent(this,xxxService.class);
startService(intent);
  • 隐式启动
    主要用于启动其他应用的Service。
    Intent设置action,可以是服务的全路径名,此时android:exported默认为true
Intent intent=new Intent();
intent.setAction("com.android.xxxService");
startService(intent);

隐式启动在4.4版本会警告,5.0以上会抛异常,因为隐式声明intent去启动Service是不安全的。

  • 需要同时设置action和packageName
    就是加上
intent.setPackage(getPackageName());//设置应用包名
  • 转换为显式启动
    需要为intent设置component再启动服务。
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
    // Retrieve all services that can match the given intent
     PackageManager pm = context.getPackageManager();
     List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
     // Make sure only one match was found
     if (resolveInfo == null || resolveInfo.size() != 1) {
         return null;
     }
     // Get component info and create ComponentName
     ResolveInfo serviceInfo = resolveInfo.get(0);
     String packageName = serviceInfo.serviceInfo.packageName;
     String className = serviceInfo.serviceInfo.name;
     ComponentName component = new ComponentName(packageName, className);
     // Create a new intent. Use the old one for extras and such reuse
     Intent explicitIntent = new Intent(implicitIntent);
     // Set the component to be explicit
     explicitIntent.setComponent(component);
     return explicitIntent;
    }

参考文章

zejian_-关于Android Service真正的完全详解,你需要知道的一切

你可能感兴趣的:(二 Service总结)