接上篇 Android 进阶解密阅读笔记4 内容,以下代码是基于 API 28 版本进行的分析,分析思路还是参阅的「Android 进阶解密」,不过书上好像有个小错误,所以我就参照着书本做的分析。
//ActiveServices
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags,String callingPackage, final int userId) throws TransactionTooLargeException {
//获取当前应用进程信息
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
//获取相关的 Service 信息
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg, isBindExternal, allowInstant);
ServiceRecord s = res.record;
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
//按 startService 的流程启动 Service
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
//这里 Service 与 应用进程建立了 binder 通信
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {}
// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
}
假设当前应用绑定了这个 Service, callerApp 就代表着当前应用进程信息,接下去会通过 retrieveServiceLocked 方法生成一个 ServiceLookupResult 对象,这个方法的大致逻辑是通过调用者进程先从缓存里找到关联的 ServiceRecord,如果没有就新建,并存入缓存。从 ServiceLookupResult 对象中能取到 ServiceRecord s。
再往下同样的逻辑可以取到 AppBindRecord 对象 b,以及新建的 ConnectionRecord 对象 c。
接着会自动创建 Service,也就是调 bringUpServiceLocked 方法走 startService 那套流程,正常启动时方法返回 null,不是 null 的话就是有问题,那么绑定过程就中止了。
再往下会,s.app 是 ProcessRecord 类型,在前面执行 realStartServiceLocked 方法时会赋值,意思是 Service 所运行的应用进程,那么这里就是指 callerApp 对象,如果前面能正常启动,那这里就不为 null。b.intent 是 IntentBindRecord 类型,b.intent.received 为 true 表示应用进程收到了绑定回调,可以获取到 Binder 对象了。
假设这是首次,应该还没收到,那么再去看下个判断,b.intent.requested 为 true 表示已发起绑定请求。此时还没有发起绑定,所以进入语句内执行 requestServiceBindingLocked 方法,
//ActiveServices
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {
//前面正是因为 !i.requested 为 true 才执行到这的,所以前半部条件满足
//i.apps 在前面获取 AppBindRecord 对象 b 时用过,可以回过头看下,就是 Service 关联的应用进程集合
//可见条件满足
if((!i.requested || rebing) && i.apps.size() > 0) {
try {
//熟悉的操作,这里通过相关联的应用进程的 ApplicationThread 继续处理
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
if(!rebind) {
//你看,这里就把请求标记至为 true 了
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
}
}
}
//ApplicationThread 会通过消息机制切到 ActivityThread 上执行
//ActivityThread
private void handleBindService(BindServiceData data) {
//从缓存中取出相应 service,这是之前执行 realStartServiceLocked 时存进去的
Service s = mServices.get(data.token);
if(s != null) {
try {
if(!data.rebind) {
//第一次会走这里,想想 onBind 方法不就是我们实现 Service 的时候要做的么
IBinder binder = s.onBind(data.intent);
ActivityManager.getService.publishService(data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManager.getService.serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
}
}
}
//往 AMS 过一下,又回到了 ActiveServices 并调用了 publishServiceLocked 方法
//ActiveServices
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
if (r != null) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
//这里 r.bindings 在之前获取 AppBindRecord 对象 b 的时候用过,所以这里可以取到
IntentBindRecord b = r.bindings.get(filter);
if(b != null && !b.received) {
//此时请求绑定已经执行,需要等待回调,所以 b.received 为 false
b.binder = service;
b.requested = true;
b.received = true;
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList clist = r.connections.valueAt(conni);
for (int i=0; i
同样的思路,如果最开始时条件 (s.app != null && b.intent.received) 满足的话,那么就会直接去连接并做 rebind 操作。