根据《Activity跨进程启动流程源码探究》我们可以清楚以下几点:
1)Context的通用实现是在ContextIml这个类中
2)Activity的启动过程需要借助ActivityManagerService(AMS)这个服务端来完成,其本质是通过Binder通信。
3)Binder通信很重要,Context作为客户端向AMS发起start请求,AMS作为服务端接收到请求后启动目标进程用于跨进程通信。AMS借助IApplicationThread引用发起目标进程Activity启动请求,注意IApplicationThread是新进程的主线程的子类ApplicationThread的代理接口,但是这些耗时操作是在Binder线程完成的。
4)在新进程中通过H这个Handler进行线程切换,并且切回了主线程。Instrumentation、Application、Activity、ContextImpl、Window、DecorView、WindowManager、ViewRoot等实例创建都在主线程中完成
鉴于此,我们分析Service的绑定启动过程从ContextImpl的bindService方法开始分析。
1.源进程发起绑定任务
在员进程的ContextImpl类中提供了bindService和bindServiceAsUser两种方法启动Service,后者提供了两个重载方法,他们最终都会调用bindServiceCommon方法。通过对比bindService和bindServiceAsUser方法的时候不难发现,他们都同时传了mMainThread.getHandler()这个Handler,追踪代码会发现它就是我们ActivityThread中的H,最终传给 LoadedApk.ServiceDispatcher的构造方法,用于切换到主线程。先来看一下bindServiceCommon这个方法,它完成了两个任务:
1)将H这个handler对象传递给LoadedApk.ServiceDispatcher的构造方法,创建IServiceConnection实例,其实是其内部的ServiceDispatcher实例。IServiceConnection是Binder代理接口,IServiceConnection.Stub存根的派生类的具体实现是LoadedApk.ServiceDispatcher的静态内部类InnerConnection。这一调用流程的核心代码如下:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
这段代码的意图其实很明显,就是为后期调用ServiceConnection#onServiceConnected方法后切回源进程主线程做准备。点击进入getServiceDispatcher的实现会看到,传入的参数被用来构建ServiceDispatcher,核心代码如下:
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
...
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c)
}
...
return sd.getIServiceConnection();
}
}
来看一下ServiceDispatcher的构造方法如下:
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
mIServiceConnection = new InnerConnection(this);
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
mIServiceConnection = new InnerConnection(this) 说明IServiceConnection的最终实现是ServiceDispatcher.InnerConnection,然后在getServiceDispatcher返回了sd.getIServiceConnection()这个方法,它当然就是mIServiceConnection这个InnerConnection的实例。
注意,Handler被命名为mActivityThread,并通过activityThread这个Handler变量赋值,就是为了提醒用户,要切回到源主线程。这是在为Service绑定成功后,调用源进程的ServiceConnection#onServiceConnected方法做准备。当然,ServiceConnection#onServiceConnected方法在主线程执行,需要通过mActivityThread这个主线程Handler切回到主线程。
2)调用ActivityManager.getService().bindService方法,将sd这个Binder实例作为参数传入,开启Binder线程。跟我们之前的分析一致,ContextImpl这个代理或者叫客户端开始通知AMS这个服务端发起执行绑定任务了。
2.目标进程启动
直接来看bindServiceCommon方法中的核心代码部分,如下:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;
...
if (mPackageInfo != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
...
try {
...
int res = ActivityManager.getService().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier());
...
return res != 0;
}
}
其实只有一行,调用了ActivityManager.getService().bindService方法,并且把sd这个IServiceConnection也传了进去,这里ContextImpl这个客户端通过bindService这个方法向AMS这个服务端发起了启动请求。Binder工作正式启动,接下来就是AMS的表演时间了~
AMS#bindService并无特殊之处,将任务交给了ActiveServices的bindServiceLocked方法,代码如下:
return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId);
注意,这里的service跟启动Activity时一致,就是Intent的实例,用于从ProcessRecord中查询是否有源进程记录。ActiveServices.bindServiceLocked方法的记录查询逻辑如下:
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);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when binding service " + service);
}
// 这里说明绑定工作是由Activity发起的,其他组件不行。
ActivityRecord activity = null;
if (token != null) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
......
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
if (res == null) {
return 0;
}
if (res.record == null) {
return -1;
}
ServiceRecord s = res.record;
通过调用内部方法 retrieveServiceLocked 返回目标进程的结果信息,service参数就是我们启动时传入的Intent,这里用于获得需要绑定的目标进程的服务信息,res.record则清晰地表明我们获取了服务记录ServiceRecord实例,ServiceRecord内部有1个ProcessRecord字段app,由于我们是假设目标进程未启动的状态下发起绑定服务的,所以该实例为空。
bindServiceLocked方法中的connection参数就是IServiceConnection的实例,它作为参数传给了bringUpServiceLocked方法唤起服务,调用核心代码如下:
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
...
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {
return 0;
}
}
因为是绑定创建,所以系统会自动添加BIND_AUTO_CREATE标记,注意
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
这行代码已经将IServiceConnection存入ConnectionRecord中,变量c又被存入ServiceRecord s这个变量里。在bringUpServiceLocked方法的参数s既是。在ActiveServices.bringUpServiceLocked方法里首先会判断目标进程的是否启动,代码如下:
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
if (r.app != null && r.app.thread != null) { //代码2-0
sendServiceArgsLocked(r, execInFg, false);
return null;
}
......
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
String hostingType = "service";
ProcessRecord app;
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); // 代码2-1
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
...
}
...
}
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null && !permissionsReviewRequired) { // 代码2-2
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingType, r.name, false, isolated, false)) == null) {
...
}
...
}
根据前面的分析,我们知道r.app是空的,条件不成立。继续向下执行,由于目标进程是普通进程,不会存在FLAG_ISOLATED_PROCESS标记,代码2-1所在区域代码逻辑会被执行,由于app为null,系统需要先去启动目标进程,执行代码2-2所在区域逻辑。点击进入mAm.startProcessLocked方法,发现enterPoint被赋值"android.app.ActivityThread"。说明明白进程也是先启动主线程ActivityThread。主线程赋值如下:
并且在AMS中将启动任务交给了其重载方法startProcessLocked,在该方法中调用了Process.start启动目标进程,其核心代码如下:
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
if (hostingType.equals("webview_service")) {
...
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, entryPointArgs);
}
其中entryPoint就是ActivityThread.main方法的入口。直接进入ActivityThread.main方法,它将具体任务交给了ActivityThread的attach方法,attach又把启动任务转交给AMS#attachApplication方法,它的内部又调用了attachApplicationLocked方法,这个方法有一个代码块至关重要:
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
} else {
app = null;
}
...
// Find any services that should be running in this process...
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
} catch (Exception e) {
...
}
}
mPidsSelfLocked存储了所有当前正在运行的进程信息,app是根据我们刚刚启动的进程记录ProcessRecord,它内部最终调用了mServices.attachApplicationLocked(app, processName)方法,进入mServices是ActiveServices实例,该方法内部会调用ActiveServices.realStartServiceLocked(r, app, execInFg)方法,调用流程如下:
boolean attachApplicationLocked(ProcessRecord proc, String processName)
throws RemoteException {
// Collect any services that are waiting for this process to come up.
if (mPendingServices.size() > 0) {
ServiceRecord sr = null;
try {
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
...
realStartServiceLocked(sr, proc, sr.createdFromFg);
...
}
}
} catch (RemoteException e) {
...
}
}
这样process作为一个参数传入到realStartServiceLocked完成真正的绑定服务任务。
此处引起极度舒适~
3. 执行真正的目标进程启动工作
realStartServiceLocked(r, app, execInFg)方法首先会通过调用app.thread.scheduleCreateService方法创建Service,这个逻辑跟《Service启动流程源码探究
》 的创建逻辑一致。
4. 执行绑定任务
服务启动后,realStartServiceLocked内部会执行requestServiceBindingsLocked(r, execInFg)方法,该方法又会调用requestServiceBindingLocked(r, ibr, execInFg, false)方法,内部有一句核心代码如下:
//i.requested这个条件是为了标记,如果已经bind过就不会再次绑定,也就是Service.onBind方法只执行一次
if ((!i.requested || rebind) && i.apps.size() > 0) {
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
r.app.thread仍然是IApplicationThread.stub的具体实现,也就是ActivityThread.ApplicationThread,ApplicationThread的处理绑定代码如下:
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
if (DEBUG_SERVICE)
Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
sendMessage(H.BIND_SERVICE, s);
}
跟ActivityThread.ApplicationThread#scheduleCreateService一致,通过一个静态内部类BindServiceData初始化绑定数据,通过H.BIND_SERVICE切换到主线程完成具体的绑定工作。
5.BIND_SERVICE消息处理
在主线程中处理H.BIND_SERVICE的方法是handleBindService处理也很简单,它首先执行s.onBind(data.intent)获得了IBinder实例,这里的s就是app.thread.scheduleCreateService方法创建的Service,注意:Service的onBind方法是在主线程调用的,所以不可以执行耗时任务。
然后将绑定任务又交给了AMS的publishService,核心代码如下:
if (!data.rebind) {
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);
}
刚切到主线程,立马又开启了Binder线程,这次ActivityThread.ApplicationThread又担当起了客户端的角色,向服务端AMS发起publishService请求,既然选择在Binder线程里面做,必然是相当耗时啊!
6.
看一下最后的狂欢吧,代码如下:
mServices.publishServiceLocked((ServiceRecord)token, intent, service)
AMS通过桥接方式,将任务交给了ActiveServices的publishServiceLocked方法,注意token就是s。该方法仍然只有一行关键代码如下:
try {
c.conn.connected(r.name, service, false);
}
这里的c就是上面存储的ConnectionRecord c变量,它内部的c.conn就是我们传入IServiceConnection实例,也就是InnerConnection的实例。这样我们进入LoadedApk.ServiceDispatcher.InnerConnection#connected(r.name, service, false)方法,看到如下代码:
public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service, dead);
}
}
点击进入ServiceDispatcher的connected方法,会发现如下代码:
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
这里的mActivityThread只有一个赋值在ServiceDispatcher的构造方法中,它是一个Handler,而且是我们在第1节谈到构造ServiceDispatcher实例时传入的H,mActivityThread.post清晰地告诉我们它将RunConnection切换到了主线程。
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
mIServiceConnection = new InnerConnection(this);
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
当然,我们还是要看一下RunConnection内部是如何执行的。代码如下:
public void run() {
if (mCommand == 0) {
doConnected(mName, mService, mDead);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
它的run方法调用了doConnected方法,看看下面这行代码吧:
old = mActiveConnections.get(name);
// 这里表示重复绑定没用,不会向下执行调用
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}
// If there is a new service, it is now connected.
if (service != null) {
mConnection.onServiceConnected(name, service);
}
是的,目标进程服务被绑定成功了,并且通知了源进程客户端。注意:这里的意思是ServiceConnection.onServiceConnected方法被H切换到了主线程,在主线程通知客户端连接成功,run方法内的所有任务都是在主线程工作,不能执行耗时任务。
值得注意的是,这里调用onServiceConnected方法,完成通知客户端工作,行程完整的闭环。
终于绑定成功了~
7.总结
很显然,相对于Activity的启动和Service的启动,Service的绑定任务相对复杂,回顾《Service启动流程源码探究》 发现,主要是增加了将客户端的绑定到服务端的环节。
1)Service的绑定仍然基于Binder的C/S模型,ContextImpl作为客户端向AMS发起绑定任务请求。在此之前要做好预处理工作,
对LoadedApk.ServiceDispather实例化,其本质就是将客户端H这个handler和ServiceConnection的实例封装在ServiceDispatcher中,传递给AMS,这样AMS就具备了切换回主线程的能力了。
2)同时,同时还携带了userID、ApplicationThread等客户端信息及service、flags等目标进程信息。AMS根据ApplicationThread这个键查询ProcessRecord中是否有相关Service记录,由于我们分析的是跨进程通信的首次启动,自然是查不到相关记录的。需要通过service(Intent)携带的进程信息,创建ProcessRecord实例,执行目标进程启动任务。
3)目标进程仍然是Android应用,所以先启动主线程ActivityThread,AMS通过ActivityThread.ApplicationThread这个引用执行CreateService的工作,通过H这个Handler切回主线程处理H.CREATE_SERVICE这个消息。处理过程包括:创建ContextImpl、Application、Service实例等任务。
4)目标进程启动完成,源进程客户端继续向AMS发起BindService的请求。AMS仍然通过ApplicationThread这个引用,借助H这个Handler向目标进程主线程发送H.BIND_SERVICE消息。同时切回到目标进程的主线程ActivityThread处理这个绑定消息。处理过程也很简单,调用Service的onBind方法,并返回IBinder实例用于发布服务绑定成功通知,然后将该参数传给AMS#publishService方法。注意:这个IBinder其实是ServiceRecord的一个实例,记录了我们要绑定的服务的所有信息。
5)一旦接到绑定任务,AMS#ActiveServices这个服务端就会提取ServiceRecord的服务记录信息,ConnectionRecord和IServiceConnection实例,执行绑定任务。通过Binder机制传入的H这个Handler将连接任务切回主线程。如果绑定成功,系统会调用客户端的mConnection.onServiceConnected接口通知客户端。onServiceConnected在主线程工作,不可以进行耗时操作。
诗云: