Service通过onBind启动流程源码探究
根据《Activity启动流程源码探究》我们可以清楚以下几点:
1)Context的通用实现是在ContextIml这个类中
2)Activity的启动过程需要借助ActivityManagerService(AMS)这个服务端来完成,其本质是通过Binder通信
3)Binder通信使用了2次,第一次Context作为客户端向AMS发起start请求,第二次AMS作为客户端向IApplicationThread发起最终的启动请求,我们暂且称为“双Binder切换机制”。
4)第二次Binder通信后,通过H这个Handler进行线程切换,并且切回了主线程。Application、Activity、ContextImpl等实例创建都在主线程中,那些耗时操作其实是在Binder线程完成的。
鉴于此,我们分析Service的绑定启动过程从ContextImpl的bindService方法开始分析。
1.第一次Binder机制
在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变量赋值,就是为了便于提醒,要切回到主线程。
虽然这里先开了一个Binder线程,其实它什么都没干,就是在为Service绑定成功后的调用ServiceConnection#onServiceConnected方法做准备工作。当然,ServiceConnection#onServiceConnected方法在子线程执行,需要通过mActivityThread这个主线程Handler切回到主线程。
2)调用ActivityManager.getService().bindService方法,开启第二个Binder线程。当然,这里就顺畅多了,跟我们之前的分析一致,ContextImpl这个代理或者叫客户端开始通知AMS这个服务端发起执行绑定任务了。
2.第二次Binder机制
直接来看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);
注意,这里的connection参数就是IServiceConnection。在bindServiceLocked内部会调用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既是。在bringUpServiceLocked方法里会调用realStartServiceLocked(r, app, execInFg)方法(注意,这个r就是刚刚的s参数)。
3.第三次Binder机制
realStartServiceLocked(r, app, execInFg)方法首先会通过调用app.thread.scheduleCreateService方法创建Service,这个逻辑跟《Service启动流程源码探究
》 的创建逻辑一致。ActiveServices是客户端向ApplicationThread这个服务端发起创建Service请求。这是第三次Binder机制。
4.第四次Binder机制
接着,bringUpServiceLocked内部会执行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);
这就是第四次Binder机制,这样一级一级的将r往下传递。r.app.thread仍然是IApplicationThread.stub的具体实现,也就是ActivityThread.ApplicationThread,ActiveServices作为客户端向服务端ApplicationThread发起bind请求,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线程里面做,必然是相当耗时啊!第五次Binder机制的开始了。
6.第五次Binder机制
看一下最后的狂欢吧,代码如下:
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方法在Binder工作线程执行连接成功后,被H切换到了主线程。
终于绑定成功了~
7.总结
很显然,相对于Activity的启动和Service的启动,Service的绑定任务相对复杂,回顾《Service启动流程源码探究》 可以发现Service绑定任务多了2次Binder机制。分别是ContextImpl作为客户端LoadedApk作为服务端时的Binder机制,和AMS作为客户端ApplicationThread作为服务端的Binder机制。那么,整个流程就很明朗了,就是5次Binder机制。
第1次Binder机制,ContextImpl作为客户端LoadedApk.ServiceDispather.InnerConnection作为服务端创建IServiceConnection实例,其本质就是将H这个handler和ServiceConnection封装在ServiceDispatcher中。
第2次Binder机制,ContextImpl作为客户端向AMS这个服务端发起绑定请求,并将IServiceConnection实例传给AMS。这样AMS就具备了切换回主线程的H了。
第3次Binder机制,AMS作为客户端向ActivityThread.ApplicationThread这个服务端发起CreateService的请求,ActivityThread.ApplicationThread接到请求后,通过H这个Handler切回主线程处理H.CREATE_SERVICE这个消息。处理过程包括:创建ContextImpl、Application、Service等任务。
第4次Binder机制,创建完任务之后,AMS作为客户端向ActivityThread.ApplicationThread这个服务端发起BindService的请求,ApplicationThread接到请求后,通过H这个Handler切回主线程处理H.BIND_SERVICE这个消息。处理过程相当简单,调用Service的onBind方法返回IBinder实例,然后将该参数传给AMS#publishService方法。注意:这个IBinder其实是ServiceRecord的一个实例,记录了我们要绑定的服务的所有信息。这样就进入第5次Binder机制了。
第5次Binder机制,一旦接到绑定任务,AMS#ActiveServices这个服务端就会提取ServiceRecord的服务信息,ConnectionRecord和IServiceConnection实例,执行绑定任务,最后通过第2次Binder机制传入的H这个Handler将连接结果回传给主线程。如果绑定成功,系统会调用客户端的mConnection.onServiceConnected接口通知客户端。
诗云:
早岁那知世事艰,中原北望气如山。楼船夜雪瓜洲渡,铁马秋风大散关。
塞上长城空自许,镜中双鬓已先斑。出师一表真名世,千载谁堪伯仲间!
笔者在写这篇文章的时候,参考了任玉刚老师《Android开发艺术探索》第9章中四大组件的启动相关内容,请知悉~