Android进阶解密
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

4.3 Service的绑定过程

我们可以通过调用Context的startService 来启动Service,也可以通过Context的bindService来绑定Service,绑定Service的过程要比启动Service的过程复杂一些,建议阅读本节前先阅读上一节Service的启动过程,结合着Service的启动过程会有更好的理解。关于如何绑定Service这种基础的问题,这里并不会讲解。Service 的绑定过程将分为两个部分来进行讲解,分别是ContextImpl到AMS的调用过程和Service的绑定过程。

4.3.1 ContextImpl到AMS的调用过程

ContextImpl到AMS的调用过程如图4-9所示。

我们可以用bindService方法来绑定Service,它在ContextWrapper中实现,代码如下所示:

图4-9 ContextImpl到AMS的调用过程

在4.2.1节我们得知mBase具体就是指向ContextImpl的,接着查看ContextImpl的bindService方法:

在bindService方法中,又返回了bindServiceCommon方法,代码如下所示:

在注释1处调用了LoadedApk类型的对象mPackageInfo的getServiceDispatcher方法,它的主要作用是将ServiceConnection封装为IServiceConnection类型的对象sd,从IServiceConnection的名字我们就能得知它实现了Binder机制,这样Service的绑定就支持了跨进程。接着在注释2处我们又看见了熟悉的代码,最终会调用AMS的bindService方法。

4.3.2 Service的绑定过程

AMS的bindService方法代码如下所示:

bindService方法最后会调用ActiveServices类型的对象mServices的bindServiceLocked方法:

讲到这里,有必要先介绍几个与Service相关的对象类型,这样有助于对源码进行理解,如下所示。

· ServiceRecord:用于描述一个Service。

· ProcessRecord:一个进程的信息。

· ConnectionRecord:用于描述应用程序进程和Service建立的一次通信。

· AppBindRecord:应用程序进程通过Intent绑定Service时,会通过AppBindRecord来维护Service与应用程序进程之间的关联。其内部存储了谁绑定的Service (ProcessRecord)、被绑定的Service (AppBindRecord)、绑定Service的Intent (IntentBindRecord)和所有绑定通信记录的信息(ArraySet<ConnectionRecord>)。

· IntentBindRecord:用于描述绑定Service的Intent。

在注释1处调用了ServiceRecord的retrieveAppBindingLocked方法来获得AppBindRecord,retrieveAppBindingLocked 方法内部创建IntentBindRecord,并对IntentBindRecord的成员变量进行赋值,后面我们会详细介绍这个关键的方法。

在注释2处调用bringUpServiceLocked方法,在bringUpServiceLocked方法中又调用realStartServiceLocked 方法,最终由ActivityThread 来调用Service的onCreate 方法启动Service,这也说明了bindService方法内部会启动Service,启动Service这一过程在4.2.2节中已经讲过,这里不再赘述。在注释3处s.app!=null 表示Service 已经运行,其中s 是ServiceRecord类型对象,app是ProcessRecord类型对象。b.intent.received表示当前应用程序进程已经接收到绑定Service 时返回的Binder,这样应用程序进程就可以通过Binder 来获取要绑定的Service的访问接口。在注释4处调用c.conn的connected方法,其中c.conn指的是IServiceConnection,它的具体实现为ServiceDispatcher.InnerConnection,其中ServiceDispatcher是LoadedApk的内部类,InnerConnection的connected方法内部会调用H的post方法向主线程发送消息,并且解决当前应用程序进程和Service跨进程通信的问题,在后面会详细介绍这一过程。在注释5处如果当前应用程序进程是第一个与Service进行绑定的,并且Service已经调用过onUnBind方法,则需要调用注释6处的代码。在注释7处如果应用程序进程的Client端没有发送过绑定Service的请求,则会调用注释8处的代码,注释8处和注释6处的代码区别就是最后一个参数rebind为false,表示不是重新绑定。接着我们查看注释6处的requestServiceBindingLocked方法,代码如下所示:

注释1处i.requested表示是否发送过绑定Service的请求,从bindServiceLocked方法的注释5处得知是发送过的,因此,!i.requested为false。从bindServiceLocked方法的注释5处得知rebind值为true,那么(!i.requested||rebind)的值为true。i.apps.size()>0表示什么呢?其中i是IntentBindRecord 类型的对象,AMS 会为每个绑定Service的Intent分配一个IntentBindRecord类型对象,代码如下所示:

我们来查看IntentBindRecord 类,不同的应用程序进程可能使用同一个Intent来绑定Service,因此在注释1处会用apps来存储所有用当前Intent绑定Service的应用程序进程。i.apps.size() > 0表示所有用当前Intent绑定Service的应用程序进程个数大于0,下面来验证i.apps.size()>0是否为ture。我们回到bindServiceLocked方法的注释1处,ServiceRecord的retrieveAppBindingLocked方法如下所示:

注释1处创建了IntentBindRecord,注释2处根据ProcessRecord获得IntentBindRecord中存储的AppBindRecord,如果AppBindRecord不为null就返回,如果为null就在注释3处创建AppBindRecord,并将ProcessRecord作为key,AppBindRecord作为value保存在IntentBindRecord的apps(i.apps)中。回到requestServiceBindingLocked方法的注释1处,结合ServiceRecord的retrieveAppBindingLocked方法,我们得知i.apps.size()>0为true,这样就会调用注释2处的代码,r.app.thread的类型为IApplicationThread,它的实现我们已经很熟悉了,是ActivityThread的内部类ApplicationThread,scheduleBindService方法如下所示:

首先将Service的信息封装成BindServiceData对象,BindServiceData的成员变量rebind的值为false,后面会用到它。接着将BindServiceData传入到sendMessage方法中。sendMessage向H发送消息,我们接着查看H的handleMessage方法:

H 在接收到BIND_SERVICE类型消息时,会在handleMessage方法中会调用handleBindService方法:

在注释1处获取要绑定的Service。注释2处的BindServiceData的成员变量rebind的值为false,这样会调用注释3处的代码来调用Service的onBind方法,到这里Service处于绑定状态了。如果rebind的值为true就会调用注释5处的Service的onRebind方法,这一点结合前文的bindServiceLocked方法的注释5处,得出的结论就是:如果当前应用程序进程第一个与Service进行绑定,并且Service已经调用过onUnBind方法,则会调用Service的onRebind方法。handleBindService方法有两个分支,一个是绑定过Servive的情况,另一个是未绑定的情况,这里分析未绑定的情况,查看注释4处的代码,实际上是调用AMS的publishService方法。讲到这里,先给出这一部分的代码时序图(不包括Service启动过程),如图4-10所示。

图4-10 Service的绑定过程前半部分调用关系时序图

接着来查看AMS的publishService方法,代码如下所示:

在publishService方法中调用了ActiveServices类型的mServices对象的publishServiceLocked方法:

注释1处的代码在前面介绍过,c.conn指的是IServiceConnection,它是ServiceConnection在本地的代理,用于解决当前应用程序进程和Service跨进程通信的问题,具体实现为ServiceDispatcher.InnerConnection,其中ServiceDispatcher是LoadedApk的内部类,ServiceDispatcher.InnerConnection的connected方法的代码如下所示:

在注释1处调用了ServiceDispatcher类型的sd对象的connected方法,代码如下所示:

在注释1处调用Handler类型的对象mActivityThread的post方法,mActivityThread实际上指向的是H。因此,通过调用H的post方法将RunConnection对象的内容运行在主线程中。RunConnection是LoadedApk的内部类,定义如下所示:

在RunConnection的run方法中调用了doConnected方法:

在注释1处调用了ServiceConnection 类型的对象mConnection 的onServiceConnected方法,这样在客户端实现了ServiceConnection接口类的onServiceConnected方法就会被执行。至此,Service 的绑定过程就分析完成。最后给出剩余部分的代码时序图,如图4-11所示。

图4-11 Service的绑定过程剩余部分的代码时序图