這篇主要講BindService的主要邏輯 Api-27
bindService相比startService無非就是多了個可以持有Service的引用,那我們就針對這個功能作為切入點.
先看下BindService的使用流程
假設(shè)一個實現(xiàn)了AIDL的接口IDoSth.aidl
interface IDoSth {
void doSth();
}
調(diào)用流程:
Context.bindService(intent, connection, Service.BIND_AUTO_CREATE);
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IDoSth currentService = IDownloadService.Stub.asInterface(service);
try {
currentService.doSth();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
...
}
};
開始源碼走起.
bindservice的入口為ContextImpl.bindService(Intent service, ServiceConnection conn, int flags);
然后ContextImpl.bindServiceCommon().
bindService和startService使用區(qū)別在于啟動者是可以持有Service的渠道的,這個渠道就是IServiceConnection
ContextImpl:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) {
IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
// 拒絕隱式Intent
validateServiceIntent(service);
try {
...
//3.調(diào)用服務(wù)端bindService
int res = ActivityManager.getService().bindService(..., sd, ...);
...
}
}
在bindServiceCommon這個方法里先是根據(jù)conn去獲得IServiceConnection sd.sd最終獲得的是LoadedApk的一個靜態(tài)內(nèi)部類InnerConnection.這個InnerConnection繼承了一個Stub,理解過AIDL都知道這是一個用于進程間通信的Binder.
private static class InnerConnection extends IServiceConnection.Stub{
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}
InnerConnection的connected方法是去調(diào)用ServiceDispatcher的doConnected()方法,然后就調(diào)用ServiceConnection.onServiceConnected(),這樣一個正常的綁定Service的流程就結(jié)束了.先得出一個結(jié)論,那就是IServiceConnection是用于服務(wù)端回調(diào)客戶端的接口,最終服務(wù)端會把IBinder傳回給客戶端讓客戶端持有服務(wù)端Binder的句柄,即上述代碼:
IDoSth currentService = IDownloadService.Stub.asInterface(service);//service就是IBinder
準備好IServiceConnection之后,就去調(diào)用服務(wù)端AMS的bindService()
服務(wù)端:
ActivityManagerService:
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
...
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
...
}
這里的mServices就是AMS中用于管理Service啟動的ActiveServices
ActiveServices:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
...
//1.獲得要啟動的Service信息
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
...
ServiceRecord s = res.record;
...
//2.得到AppBindRecord,AppBindRecord描述的作用是連接Service和他的綁定者(IntentBindRecord)
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
//3.啟動Service
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {
return 0;
}
}
...
//4.service如果準備好之后就可以調(diào)用ServiceConnection.connected()了
if (s.app != null && b.intent.received) {
//Service已經(jīng)運行, 我們可以立即發(fā)布connection.
try {
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
...
}
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
//5.
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
}
bindServiceLocked()這個方法里大致是這么個邏輯:
- 首先通過retrieveServiceLocked()獲得將要啟動的Service信息
- 然后去找IntentBindRecord,AppBindRecord.這里的操作是去做一系列綁定信息的添加.
- 這里如果目標Service還沒啟動,就會調(diào)用bringUpServiceLocked()去啟動Service并且返回非空,那么就直接return. 0,下面就不用執(zhí)行了,原因是啟動Service是個異步操作,這樣代碼就不好繼續(xù)下去.
- 這里有兩個判斷,一個是s.app != null && b.intent.received.這個received是在publishServiceLocked()之后才會設(shè)為true.所以第一次發(fā)布會跳過4,進入5
- requestServiceBindingLocked()會先調(diào)r.app.thread.scheduleBindService()進入客戶端調(diào)用ActivityThread.handleBindService()然后又會回到服務(wù)端,最終回到ActiveServices.publishServiceLocked().在這個方法里才把received設(shè)為true并調(diào)用c.conn.connected()最終觸發(fā)ServiceConnection.onServiceConnected()這個開頭有分析過.
ActiveServices:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
try {
...
b.requested = true;
b.received = true;
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
...
try {
c.conn.connected(r.name, service, false);
} catch (Exception e) {
}
...
}
}
...
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
...
return true;
}
至此,這就是BindService的持有引用的過程.