对应用开发者而言,bindService调用肯定是耳熟能详,本文将介绍framework如何表示和组织绑定关系,通过背后原理的分析来加深对Service的理解,除此之外,大家也能领略到框架代码是如何设计并带来一些启发。本文旨在理清主要的设计思路,并不会对所有细节展开描述,代码来自Android N。
让我们回顾下bindService相关的知识点,这里以在Activity中调用bindService函数为例,函数原型如下:
public boolean bindService(Intent service, ServiceConnection conn,
int flags)
指定目标Service的Intent,当目标Service的onBind()方法执行后,传进来的ServiceConnection接口就会得到回调,之前Service.onBind()返回的IBinder对象会作为参数传进来。从这个过程可以看到,系统要管理Service以及Service的使用方,维护它们之间的关系并在合适的时机执行某些回调。
bindService的最终实现在ContextImpl.java中,经过一系列处理调用到AMS.bindService方法,函数原型如下:
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId)
ContextImpl封装了很多信息,很容易就可以拿到诸如调用方进程的标识(IApplicationThread),以及调用方Activity的标识(token),这些信息是维护Service绑定关系的一部分材料,后面会再提到。
这里主要提下IServiceConnection参数,看命名好像跟ServiceConnection有关,但ServiceConnection是普通的接口,而IServiceConnection是Binder接口,应该有着某种映射关系,做为Service绑定的一个重要标识,有必要认清这个映射关系。
LoadedApk.java中的如下成员维护了该映射关系:
private final ArrayMap> mServices
= new ArrayMap>();
LoadedApk.ServiceDispatcher封装了ServiceConnection并包含一个IServiceConnection,可以认为LoadedApk.ServiceDispatcher和IServiceConnection是一对一的关系,Binder代理端通过调用IServiceConnection的接口,找到对应的ServiceConnection。
即:指定一个Context和ServiceConnection,有且只有一个IServiceConnection与之对应。
也就意味着,在不同的Activity中用同一个ServiceConnection调用bindService,最后传给AMS的是不同的IServiceConnection。其它推论可自行推导。
先了解下AMS中几个类的含义,这在后面的内容中要用到:
绑定关系至少包含如下信息:
ConnectionRecord用来表示绑定关系,即每次调用bindService都会生成该类对象,接下来讨论它是如何包含上述信息。
先看下它的成员变量:
/**
* Description of a single binding to a service.
*/
final class ConnectionRecord {
final AppBindRecord binding; // The application/service binding.
final ActivityRecord activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
final PendingIntent clientIntent; // How to launch the client.
String stringName; // Caching of toString.
boolean serviceDead; // Well is it?
}
关注前面四个成员,activity,conn,flags很好理解:
* activity:发起绑定的Activity
* conn:对应的IServiceConnection
* flags:绑定选项
就这点信息显然不够,别忘了还有个AppBindRecord,那为什么不像其它信息那样逐项列出来,答案是为了方便在不同层面上对ConnectionRecord进行管理。要理解有哪些层面,我们先来看一个例子。
假设有一个Service,配置如下:
<service android:name="MyService">
<intent-filter>
<action android:name="com.example.sea.myapplication.MyService" />
intent-filter>
<intent-filter>
<action android:name="com.example.sea.myapplication.MyService2" />
intent-filter>
service>
然后在Activity中执行如下代码,用不同的Action来调用bindService:
Intent intent = new Intent("com.example.sea.myapplication.MyService");
intent.setPackage("com.example.sea.myapplication");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
intent.setAction("com.example.sea.myapplication.MyService2");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
问题:MyService.onBind()会执行几次?
答案是两次,应用可以根据传入Intent的Action不同来返回不同的IBinder。
这就引申出framework关于Service绑定的设计:对同一个Service而言,不同的Intent代表不同的绑定关系,每个Intent对应一个IBinder,Intent是否相同用下面的函数来判断,true表示相同:
public boolean filterEquals(Intent other) {
if (other == null) {
return false;
}
if (!Objects.equals(this.mAction, other.mAction)) return false;
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mPackage, other.mPackage)) return false;
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
return true;
}
那么ServiceRecord管理ConnectionRecord,首先应该以Intent进行映射,如下:
final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
= new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
Intent.FilterComparison是对Intent的封装,用来判断Intent是否相等以及计算哈希值,可以简单地认为是一对一关系。不同的Intent对应不同的IntentBindRecord,按照上述例子的结论,IntentBindRecord应该保存有IBinder:
/**
* A particular Intent that has been bound to a Service.
*/
final class IntentBindRecord {
/** The running service. */
final ServiceRecord service;
/** The intent that is bound.*/
final Intent.FilterComparison intent; //
/** All apps that have bound to this Intent. */
final ArrayMap apps
= new ArrayMap();
/** Binder published from service. */
IBinder binder;
/** Set when we have initiated a request for this binder. */
boolean requested;
/** Set when we have received the requested binder. */
boolean received;
/** Set when we still need to tell the service all clients are unbound. */
boolean hasBound;
/** Set when the service's onUnbind() has asked to be told about new clients. */
boolean doRebind;
}
binder成员即Service.onBind()对不同Intent返回的IBinder,和预想的一致。
接下来还需要如何组织ConnectionRecord?注意到IntentBindRecord 的apps成员,类型为:
ArrayMap<ProcessRecord, AppBindRecord>
还记得ConnectionRecord.binding成员吗,就是这里的AppBindRecord,它跟ProcessRecord形成映射关系。也就是说在Intent分类下,以bindService调用方进程(ProcessRecord)再进行分类:
/**
* An association between a service and one of its client applications.
*/
final class AppBindRecord {
final ServiceRecord service; // The running service.
final IntentBindRecord intent; // The intent we are bound to.
final ProcessRecord client; // Who has started/bound the service.
final ArraySet connections = new ArraySet<>();
// All ConnectionRecord for this client.
}
经过这样的组织,从ConnectionRecord出发有如下引用链:
ConnectionRecord->AppBindRecord->IntentBindRecord->ServiceRecord
引用链上对象包含的成员均能得到访问,通过上面类定义很容易知道这里面包含了关于绑定关系的所有信息。
通过这样的设计,将ConnectionRecord按不同的层面进行分类,使得结构很清晰;另外,不同层面上的对象可以得到复用,比如AppBindRecord和IntentBindRecord。
到这里为止,我们已经得到了一个ConnectionRecord,它包含了关于绑定的所有信息。接下来还要将其按照一定规则组织起来,以方便遍历和查询。
1、ServiceRecord要知道所有关于它的ConnectionRecord,于是在ServiceRecord.java中有:
final ArrayMap> connections
= new ArrayMap>();
// IBinder -> ConnectionRecord of all bound clients
2、要查询指定Intent下某个ProcessRecord的所有ConnectionRecord,于是在AppBindRecord.java中有:
final ArraySet connections = new ArraySet<>();
// All ConnectionRecord for this client.
3、要查询某个应用进程执行了几次绑定,于是在ProcessRecord.java中有:
// All ConnectionRecord this process holds
final ArraySet connections = new ArraySet<>();
4、要查询某个Activity执行了几次绑定,于是在ActivityRecord.java中有:
HashSet connections; // All ConnectionRecord we hold
5、要通过IServiceConnection全局查询某个ConnectionRecord,于是在ActiveServices.java中有:
/**
* All currently bound service connections. Keys are the IBinder of
* the client's IServiceConnection.
*/
final ArrayMap> mServiceConnections = new ArrayMap<>();
既然有绑定,相应的就要有解绑,解绑的过程自然是要解除上述的组织关系,代码位于ActiveServices.java:
boolean unbindServiceLocked(IServiceConnection connection) {
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
ArrayList clist = mServiceConnections.get(binder);
if (clist == null) {
Slog.w(TAG, "Unbind failed: could not find connection for "
+ connection.asBinder());
return false;
}
final long origId = Binder.clearCallingIdentity();
try {
while (clist.size() > 0) {
ConnectionRecord r = clist.get(0);
removeConnectionLocked(r, null, null);
if (clist.size() > 0 && clist.get(0) == r) {
// In case it didn't get removed above, do it now.
Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
clist.remove(0);
}
if (r.binding.service.app != null) {
if (r.binding.service.app.whitelistManager) {
updateWhitelistManagerLocked(r.binding.service.app);
}
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
r.binding.service.app.treatLikeActivity = true;
mAm.updateLruProcessLocked(r.binding.service.app,
r.binding.service.app.hasClientActivities
|| r.binding.service.app.treatLikeActivity, null);
}
mAm.updateOomAdjLocked(r.binding.service.app);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return true;
}
首先以connection为键在mServiceConnections中查询对应的ConnectionRecord列表,然后对于每个ConnectionRecord,调用removeConnectionLocked进行移除:
void removeConnectionLocked(
ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
ArrayList clist = s.connections.get(binder);
if (clist != null) {
// 从ServiceRecord.connections中移除
clist.remove(c);
if (clist.size() == 0) {
s.connections.remove(binder);
}
}
b.connections.remove(c); // 从AppBindRecord.connections中移除
if (c.activity != null && c.activity != skipAct) {
if (c.activity.connections != null) {
// 从ActivityRecord.connections中移除
c.activity.connections.remove(c);
}
}
if (b.client != skipApp) {
// 从ProcessRecord.connections中移除
b.client.connections.remove(c);
...
}
clist = mServiceConnections.get(binder);
if (clist != null) {
// 从ActiveServices.mServiceConnections中移除
clist.remove(c);
if (clist.size() == 0) {
mServiceConnections.remove(binder);
}
}
mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
}
...
}
代码仅关注绑定关系移除部分,详见上面的注释。