从 Binder 通信机制角度谈 bindService 的启动
2021-02-07 本文已影响0人
jkwen
前面按照书本内容走了一遍 bindService 启动梳理,Android 进阶解密阅读笔记5 那时的重点在于流程是怎么走的,这篇我准备从参与 Binder 机制的过程重新梳理下,侧重点在于客户端(也就是调用 bindService 方法的一方)是如何与服务端(提供服务功能的一方)建立通信连接的。
//从 ContextImpl 的 bindService 方法开始
IServiceConnection sd;
if(mPackageInfo != null) {
if(executor != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
} else {
//这里会走这句代码创建 sd
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
}
//LoadedApk
private IServiceConnection getServiceDispatcherCommon(ServiceConnection c, Context context, Handler handler, Executor executor, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
//通过带参构造方法创建 sd,其中下面 return 的 IServiceContention 就是内部类的对象实例
//即 InnerConnection 的对象
sd = new ServiceDispatcher(c, context, handler, flags);
map.put(c, sd);
}
return sd.getIServiceConnection();
}
可见,当我们通过调用 bindService 方法进行 Service 绑定时,入参的 ServiceConnection 对象和 flags 标志被用来创建了 ServiceDispatcher 对象,Intent 对象被用在了创建 Service。
再按之前梳理的走下去,会触发 Service 的 onBind 方法。该方法要求返回一个 IBinder 类型的值,这个返回值其实就是服务方的 Binder 实体。也就是服务端中那个继承自 Binder 且实现服务功能接口的类的实体。
//AMS
//token 实际是 ServiceRecord 类型
//service 就是服务端提供的 Binder 实体
public void publishService(IBinder token, Intent intent, IBinder service) {
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
try {
//c.conn 就是前面我们提到的 InnerConnection 对象
//service 就是服务端的 Binder 实体
c.conn.connected(r.name, service, false);
}
}
}
}
}
//InnerConnection
public void connected(ComponentName name, IBinder service, boolean dead) {
//这里取到的就是一开始用 ServiceConnection 和 flags 创建的 ServiceDispatcher 对象
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service, dead);
}
}
//ServiceDispatcher
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityExecutor != null) {
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
//接下去会执行这句代码,
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
//RunConnection
public void run() {
if (mCommand == 0) {
doConnected(mName, mService, mDead);
} else if(mCommand == 1) {
}
}
//ServiceDispatcher
public void doConnected(ComponentName name, IBinder service, boolean dead) {
if (service != null) {
//service 是服务端的 Binder 实体
//mConnection 是一开始的入参 ServiceConnection 对象
mConnection.onServiceConnected(name, service);
} else {
mConnection.onNullBinding(name);
}
}
所以,作为客户端是通过 ServiceConnection 的 onServiceConnected 方法与服务端建立了通信,通信的介质可以认为是 IBinder 对象。
那么客户端拿到这个 IBinder 对象后又该怎么用呢?肯定不能直接用,也没法强制类型转换,具体要怎么去用呢,可以看下这篇 Binder 机制入门。