根據(jù)《Activity啟動(dòng)流程源碼探究》我們可以清楚以下幾點(diǎn):
1)Context的通用實(shí)現(xiàn)是在ContextIml這個(gè)類中
2)Activity的啟動(dòng)過(guò)程需要借助ActivityManagerService(AMS)這個(gè)服務(wù)端來(lái)完成,其本質(zhì)是通過(guò)Binder通信
3)Binder通信使用了2次贺嫂,第一次Context作為客戶端向AMS發(fā)起start請(qǐng)求悍缠,第二次AMS作為客戶端向IApplicationThread發(fā)起最終的啟動(dòng)請(qǐng)求,我們暫且稱為“雙Binder切換機(jī)制”姑荷。
4)第二次Binder通信后盒延,通過(guò)H這個(gè)Handler進(jìn)行線程切換,并且切回了主線程鼠冕。Application添寺、Activity、ContextImpl等實(shí)例創(chuàng)建都在主線程中懈费,那些耗時(shí)操作其實(shí)是在Binder線程完成的计露。
鑒于此,我們分析Service的綁定啟動(dòng)過(guò)程從ContextImpl的bindService方法開(kāi)始分析。
1.第一次Binder機(jī)制
在ContextImpl中提供了bindService和bindServiceAsUser兩種方法啟動(dòng)Service薄坏,后者提供了兩個(gè)重載方法趋厉,他們最終都會(huì)調(diào)用bindServiceCommon方法。通過(guò)對(duì)比bindService和bindServiceAsUser方法的時(shí)候不難發(fā)現(xiàn)胶坠,他們都同時(shí)傳了mMainThread.getHandler()這個(gè)Handler君账,追蹤代碼會(huì)發(fā)現(xiàn)它就是我們ActivityThread中的H,最終傳給 LoadedApk.ServiceDispatcher的構(gòu)造方法沈善,用于切換到主線程乡数。
先來(lái)看一下bindServiceCommon這個(gè)方法,它完成了兩個(gè)任務(wù):
1)將H這個(gè)handler對(duì)象傳遞給LoadedApk.ServiceDispatcher的構(gòu)造方法闻牡,創(chuàng)建IServiceConnection實(shí)例净赴,其實(shí)是其內(nèi)部的ServiceDispatcher實(shí)例。IServiceConnection是Binder代理接口罩润,IServiceConnection.Stub存根的派生類的具體實(shí)現(xiàn)是LoadedApk.ServiceDispatcher的靜態(tài)內(nèi)部類InnerConnection玖翅。這一調(diào)用流程的核心代碼如下:
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");
}
這段代碼的意圖其實(shí)很明顯,就是為后期調(diào)用ServiceConnection#onServiceConnected方法后切回主線程做準(zhǔn)備割以。點(diǎn)擊進(jìn)入getServiceDispatcher的實(shí)現(xiàn)會(huì)看到金度,傳入的參數(shù)被用來(lái)構(gòu)建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();
}
}
來(lái)看一下ServiceDispatcher的構(gòu)造方法如下:
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)說(shuō)明IServiceConnection的最終實(shí)現(xiàn)是ServiceDispatcher.InnerConnection严沥,然后在getServiceDispatcher返回了sd.getIServiceConnection()這個(gè)方法猜极,它當(dāng)然就是mIServiceConnection這個(gè)InnerConnection的實(shí)例。注意消玄,Handler被命名為mActivityThread跟伏,并通過(guò)activityThread這個(gè)Handler變量賦值,就是為了便于提醒翩瓜,要切回到主線程受扳。
雖然這里先開(kāi)了一個(gè)Binder線程,其實(shí)它什么都沒(méi)干奥溺,就是在為Service綁定成功后的調(diào)用ServiceConnection#onServiceConnected方法做準(zhǔn)備工作辞色。當(dāng)然,ServiceConnection#onServiceConnected方法在子線程執(zhí)行浮定,需要通過(guò)mActivityThread這個(gè)主線程Handler切回到主線程相满。
2)調(diào)用ActivityManager.getService().bindService方法,開(kāi)啟第二個(gè)Binder線程桦卒。當(dāng)然立美,這里就順暢多了,跟我們之前的分析一致方灾,ContextImpl這個(gè)代理或者叫客戶端開(kāi)始通知AMS這個(gè)服務(wù)端發(fā)起執(zhí)行綁定任務(wù)了建蹄。
2.第二次Binder機(jī)制
直接來(lái)看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;
}
}
其實(shí)只有一行,調(diào)用了ActivityManager.getService().bindService方法洞慎,并且把sd這個(gè)IServiceConnection也傳了進(jìn)去痛单,這里ContextImpl這個(gè)客戶端通過(guò)bindService這個(gè)方法向AMS這個(gè)服務(wù)端發(fā)起了啟動(dòng)請(qǐng)求。第二次Binder正式啟動(dòng)劲腿,接下來(lái)就是AMS的表演時(shí)間了~
AMS#bindService并無(wú)特殊之處旭绒,將任務(wù)交給了ActiveServices的bindServiceLocked方法,代碼如下:
return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, callingPackage, userId);
注意焦人,這里的connection參數(shù)就是IServiceConnection挥吵。在bindServiceLocked內(nèi)部會(huì)調(diào)用bringUpServiceLocked方法喚起服務(wù),調(diào)用核心代碼如下:
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;
}
}
因?yàn)槭墙壎▌?chuàng)建花椭,所以系統(tǒng)會(huì)自動(dòng)添加BIND_AUTO_CREATE標(biāo)記忽匈,注意
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
這行代碼已經(jīng)將IServiceConnection存入ConnectionRecord中,變量c又被存入ServiceRecord s這個(gè)變量里矿辽。在bringUpServiceLocked方法的參數(shù)s既是丹允。在bringUpServiceLocked方法里會(huì)調(diào)用realStartServiceLocked(r, app, execInFg)方法(注意,這個(gè)r就是剛剛的s參數(shù))嗦锐。
3.第三次Binder機(jī)制
realStartServiceLocked(r, app, execInFg)方法首先會(huì)通過(guò)調(diào)用app.thread.scheduleCreateService方法創(chuàng)建Service嫌松,這個(gè)邏輯跟《Service啟動(dòng)流程源碼探究
》 的創(chuàng)建邏輯一致沪曙。ActiveServices是客戶端向ApplicationThread這個(gè)服務(wù)端發(fā)起創(chuàng)建Service請(qǐng)求奕污。這是第三次Binder機(jī)制。
4.第四次Binder機(jī)制
接著液走,bringUpServiceLocked內(nèi)部會(huì)執(zhí)行requestServiceBindingsLocked(r, execInFg)方法碳默,該方法又會(huì)調(diào)用requestServiceBindingLocked(r, ibr, execInFg, false)方法,內(nèi)部有一句核心代碼如下:
//i.requested這個(gè)條件是為了標(biāo)記缘眶,如果已經(jīng)bind過(guò)就不會(huì)再次綁定嘱根,也就是Service.onBind方法只執(zhí)行一次
if ((!i.requested || rebind) && i.apps.size() > 0) {
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
這就是第四次Binder機(jī)制,這樣一級(jí)一級(jí)的將r往下傳遞巷懈。r.app.thread仍然是IApplicationThread.stub的具體實(shí)現(xiàn)该抒,也就是ActivityThread.ApplicationThread,ActiveServices作為客戶端向服務(wù)端ApplicationThread發(fā)起bind請(qǐng)求顶燕,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一致凑保,通過(guò)一個(gè)靜態(tài)內(nèi)部類BindServiceData初始化綁定數(shù)據(jù),通過(guò)H.BIND_SERVICE切換到主線程完成具體的綁定工作涌攻。
5.BIND_SERVICE消息處理
在主線程中處理H.BIND_SERVICE的方法是handleBindService處理也很簡(jiǎn)單欧引,它首先執(zhí)行s.onBind(data.intent)獲得了IBinder實(shí)例,這里的s就是app.thread.scheduleCreateService方法創(chuàng)建的Service恳谎,注意:Service的onBind方法是在主線程調(diào)用的芝此,所以不可以執(zhí)行耗時(shí)任務(wù)憋肖。
然后將綁定任務(wù)又交給了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);
}
剛切到主線程婚苹,立馬有開(kāi)啟了Binder線程岸更,這次ActivityThread.ApplicationThread又擔(dān)當(dāng)起了客戶端的角色,向服務(wù)端AMS發(fā)起publishService請(qǐng)求膊升,既然選擇在Binder線程里面做坐慰,必然是相當(dāng)耗時(shí)啊用僧!第五次Binder機(jī)制的開(kāi)始了结胀。
6.第五次Binder機(jī)制
看一下最后的狂歡吧,代碼如下:
mServices.publishServiceLocked((ServiceRecord)token, intent, service)
AMS通過(guò)橋接方式责循,將任務(wù)交給了ActiveServices的publishServiceLocked方法糟港,注意token就是s。該方法仍然只有一行關(guān)鍵代碼如下:
try {
c.conn.connected(r.name, service, false);
}
這里的c就是上面存儲(chǔ)的ConnectionRecord c變量院仿,它內(nèi)部的c.conn就是我們傳入IServiceConnection實(shí)例秸抚,也就是InnerConnection的實(shí)例。這樣我們進(jìn)入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);
}
}
點(diǎn)擊進(jìn)入ServiceDispatcher的connected方法剥汤,會(huì)發(fā)現(xiàn)如下代碼:
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只有一個(gè)賦值在ServiceDispatcher的構(gòu)造方法中,它是一個(gè)Handler排惨,而且是我們?cè)诘?節(jié)談到構(gòu)造ServiceDispatcher實(shí)例時(shí)傳入的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;
}
當(dāng)然暮芭,我們還是要看一下RunConnection內(nèi)部是如何執(zhí)行的鹿驼。代碼如下:
public void run() {
if (mCommand == 0) {
doConnected(mName, mService, mDead);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
它的run方法調(diào)用了doConnected方法,看看下面這行代碼吧:
old = mActiveConnections.get(name);
// 這里表示重復(fù)綁定沒(méi)用辕宏,不會(huì)向下執(zhí)行調(diào)用
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);
}
是的畜晰,服務(wù)被綁定成功了,并且通知了客戶端瑞筐。注意:這里的意思是ServiceConnection.onServiceConnected方法在Binder工作線程執(zhí)行連接成功后凄鼻,被H切換到了主線程。
終于綁定成功了~
7.總結(jié)
很顯然聚假,相對(duì)于Activity的啟動(dòng)和Service的啟動(dòng)块蚌,Service的綁定任務(wù)相對(duì)復(fù)雜,回顧《Service啟動(dòng)流程源碼探究》 可以發(fā)現(xiàn)Service綁定任務(wù)多了2次Binder機(jī)制魔策。分別是ContextImpl作為客戶端LoadedApk作為服務(wù)端時(shí)的Binder機(jī)制匈子,和AMS作為客戶端ApplicationThread作為服務(wù)端的Binder機(jī)制。那么闯袒,整個(gè)流程就很明朗了虎敦,就是5次Binder機(jī)制游岳。
第1次Binder機(jī)制,ContextImpl作為客戶端LoadedApk.ServiceDispather.InnerConnection作為服務(wù)端創(chuàng)建IServiceConnection實(shí)例其徙,其本質(zhì)就是將H這個(gè)handler和ServiceConnection封裝在ServiceDispatcher中胚迫。
第2次Binder機(jī)制,ContextImpl作為客戶端向AMS這個(gè)服務(wù)端發(fā)起綁定請(qǐng)求唾那,并將IServiceConnection實(shí)例傳給AMS访锻。這樣AMS就具備了切換回主線程的H了。
第3次Binder機(jī)制闹获,AMS作為客戶端向ActivityThread.ApplicationThread這個(gè)服務(wù)端發(fā)起CreateService的請(qǐng)求期犬,ActivityThread.ApplicationThread接到請(qǐng)求后,通過(guò)H這個(gè)Handler切回主線程處理H.CREATE_SERVICE這個(gè)消息避诽。處理過(guò)程包括:創(chuàng)建ContextImpl龟虎、Application、Service等任務(wù)沙庐。
第4次Binder機(jī)制鲤妥,創(chuàng)建完任務(wù)之后,AMS作為客戶端向ActivityThread.ApplicationThread這個(gè)服務(wù)端發(fā)起B(yǎng)indService的請(qǐng)求拱雏,ApplicationThread接到請(qǐng)求后棉安,通過(guò)H這個(gè)Handler切回主線程處理H.BIND_SERVICE這個(gè)消息。處理過(guò)程相當(dāng)簡(jiǎn)單铸抑,調(diào)用Service的onBind方法返回IBinder實(shí)例贡耽,然后將該參數(shù)傳給AMS#publishService方法。注意:這個(gè)IBinder其實(shí)是ServiceRecord的一個(gè)實(shí)例羡滑,記錄了我們要綁定的服務(wù)的所有信息菇爪。這樣就進(jìn)入第5次Binder機(jī)制了。
第5次Binder機(jī)制柒昏,一旦接到綁定任務(wù),AMS#ActiveServices這個(gè)服務(wù)端就會(huì)提取ServiceRecord的服務(wù)信息熙揍,ConnectionRecord和IServiceConnection實(shí)例职祷,執(zhí)行綁定任務(wù),最后通過(guò)第2次Binder機(jī)制傳入的H這個(gè)Handler將連接結(jié)果回傳給主線程届囚。如果綁定成功有梆,系統(tǒng)會(huì)調(diào)用客戶端的mConnection.onServiceConnected接口通知客戶端。
詩(shī)云:
早歲那知世事艱意系,中原北望氣如山泥耀。樓船夜雪瓜洲渡,鐵馬秋風(fēng)大散關(guān)蛔添。
塞上長(zhǎng)城空自許痰催,鏡中雙鬢已先斑兜辞。出師一表真名世,千載誰(shuí)堪伯仲間夸溶!
筆者在寫這篇文章的時(shí)候逸吵,參考了任玉剛老師《Android開(kāi)發(fā)藝術(shù)探索》第9章中四大組件的啟動(dòng)相關(guān)內(nèi)容,請(qǐng)知悉~