[Android] bindService的binder通訊過程分析
關於bindService方法
public class ContextWrapper extendsContext {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
returnmBase.bindService(service, conn, flags);
}
ContextImpl.java
// bindService
@Override
public boolean bindService(Intent service, ServiceConnection conn, intflags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn,flags, Process.myUserHandle());
}
// bindServiceCommon
private boolean bindServiceCommon(Intent service, ServiceConnectionconn, int flags, UserHandle user) {
IServiceConnection sd;
...
//包裝ServiceConnection
sd =mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(),flags);
...
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(),user.getIdentifier());
...
}
這裡是通過binder和AMS進行通訊,一次binder呼叫。
在通訊的時候,構造了一個InnerConnection的binder物件作為引數傳給了AMS,以便於實現方法回撥。
private static class InnerConnectionextends IServiceConnection.Stub {
finalWeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = newWeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentNamename, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name,service);
}
}
}
另外構造了ServiceDispatcher物件將ServiceConnection和InnerConnection進行了包裝。
在回撥的時候,AMS端作為Proxy呼叫connected方法。
在AMS中的呼叫比較複雜,時序圖如下
AMS的bindService方法
public intbindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType,IServiceConnection connection, int flags, String callingPackage,
int userId) throwsTransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked filedescriptors
if (service != null &&service.hasFileDescriptors() == true) {
throw newIllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw newIllegalArgumentException("callingPackage cannot be null");
}
synchronized(this) {
return mServices.bindServiceLocked(caller, token,service,
resolvedType, connection,flags, callingPackage, userId);
}
}
裡面呼叫到mServices.bindServiceLocked
在ActivityManagerService.java中有定義
final ActiveServices mServices;
繼續呼叫到其realStartServiceLocked方法
public final class ActiveServices {
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, booleanexecInFg) throws RemoteException {
...
r.app = app;
...
app.thread.scheduleCreateService(r,r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
...
//
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
...
}
private final boolean requestServiceBindingLocked(ServiceRecordr, IntentBindRecord i,
boolean execInFg, boolean rebind)throws TransactionTooLargeException {
if (r.app == null || r.app.thread ==null) {
// If service is not currentlyrunning, can't yet bind.
return false;
}
if ((!i.requested || rebind) &&i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r,execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
r.app.thread.scheduleBindService(r,i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch(TransactionTooLargeException e) {
……
}
}
return true;
}
由於AMS中已經儲存了系統中已啟動的應用和服務的資訊,這裡的r.app.thread是服務的一個proxy,通過這個binder呼叫,服務中的scheduleBindService方法被執行,裡面呼叫了
public final void scheduleBindService(IBindertoken, Intent intent,
boolean rebind, intprocessState) {
updateProcessState(processState,false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
sendMessage(H.BIND_SERVICE, s);
}
處理該訊息時呼叫ActivityThread的
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token,data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token,SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
throwex.rethrowFromSystemServer();
}
} catch (Exception e) {
}
}
}
在server程序中,通過呼叫AMS服務publishService把binder物件傳給AMS
這裡,再回顧下這個時序圖
可以看出,程序間的呼叫都是binder通訊呼叫。
在AMS呼叫scheduleBindService的時候,有一個很巧妙的設計,就是把RecordService作為引數傳遞給了server端,其實server端並沒有實際使用RecordService,只是把RecordService再通過publishService又傳遞給了AMS,為什麼這麼曲折呢?
因為server端的sheduleBindService方法中進行了非同步呼叫。採用這種方式,連線了RecordService和server端傳遞的服務binder的對應關係。AMS根據這個RecordService找到呼叫的client端,再通過呼叫connected方法把server的binder傳送過去。
ActivityManagerService中有方法
public void publishService(IBindertoken, Intent intent, IBinder service) {
// Refuse possible leaked filedescriptors
if (intent != null &&intent.hasFileDescriptors() == true) {
throw newIllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
if (!(token instanceofServiceRecord)) {
throw newIllegalArgumentException("Invalid service token");
}
mServices.publishServiceLocked((ServiceRecord)token,intent, service);
}
}
ActiveServices.java
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
...
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++) {
ConnectionRecord c= clist.get(i);
...
try {
//binder呼叫,AMS呼叫client的方法,把service的binder傳過去
c.conn.connected(r.name, service);
} catch (Exceptione) {
Slog.w(TAG,"Failure sending service " + r.name +
" toconnection " + c.conn.asBinder() +
"(in " + c.binding.client.processName + ")", e);
}
}
}
...
}
這裡根據ServiceRecord找到對應的IServiceConnection,
ServiceRecord中有定義
//IBinder -> ConnectionRecord of all bound clients
finalArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
= new ArrayMap<IBinder,ArrayList<ConnectionRecord>>();
由於一個service中可能會提供多個binder服務,每個binder又會有多個客戶端連線,所以這裡使用了ArrayMap<IBinder,ArrayList<ConnectionRecord>>的定義,
再通過遍歷connections找到對應的ConnectionRecord,ConnectionRecord中儲存有客戶端的IServiceConnection conn作為通訊proxy。
ConnectionRecord.java
final class ConnectionRecord {
final AppBindRecord binding; //The application/service binding.
final ActivityRecord activity; //If non-null, the owning activity.
final IServiceConnection conn; // The client connection.