2, bindService
當一個使用者程序bindService()時,它需要先準備好一個實作了ServiceConnection接口的對象。ServiceConnection的定義如下:
public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}
每當使用者調用bindService(),Android都将之視作是要建立一個新的“邏輯連接配接”。而當連接配接建立起來時,系統會回調ServiceConnection
接口的onServiceConnected()。另一方面,那個onServiceDisconnected()函數卻不是在unbindService()時發生的。一般來說,當目标
service所在的程序意外挂掉或者被殺掉時,系統才會回調onServiceDisconnected(),而且,此時并不會銷毀之前的邏輯連接配接,也就是說,
那個“邏輯連接配接”仍然處于激活狀态,一旦service後續再次運作,系統會再次回調onServiceConnected()。bindService調用流程圖如下,
程序調用bindService方法時,向AMS請求這一段和啟動服務的流程一樣,在此就不詳細分析了。調用流程圖如下,
AMS的bindService方法如下,
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
enforceNotIsolatedCaller("bindService");
•••
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
bindServiceLocked方法如下,
int bindServiceLocked(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
. . . . . .
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
. . . . . .
ActivityRecord activity = null;
. . . . . .
activity = ActivityRecord.isInStackLocked(token);
. . . . . .
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
. . . . . .
ServiceRecord s = res.record;
. . . . . .
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
. . . . . .
clist.add(c);
b.connections.add(c);
. . . . . .
b.client.connections.add(c);
. . . . . .
clist = mServiceConnections.get(binder);
. . . . . .
clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
. . . . . .
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
. . . . . .
if (s.app != null && b.intent.received) {
. . . . . .
c.conn.connected(s.name, b.intent.binder);
. . . . . .
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
. . . . . .
}
盡管這個函數裡有不少技術細節,而且涉及到多個映射表,但它的總體意思大概是這樣的。每當使用者調用bindService()時,
Android都将之看作是要建立一個新的“邏輯連接配接”,而每個邏輯連接配接都對應一個ConnectionRecord節點,是以最終的表現肯定
是向ServiceRecord内部的某張映射表裡添加一個新的ConnectionRecord節點。當然,在實際運作時,這個節點還會記錄
進其他幾個映射表裡(比如系統總映射表)。
對于一個Service而言,有多少應用和它建立了綁定關系,就會有多少個AppBindRecord節點, 當然,一個應用裡可以有多個
地方發起綁定動作,是以AppBindRecord裡需要用一個ArraySet<ConnectionRecord>記錄下每個綁定動作對應的邏輯連接配接節點。
在ConnectionRecord被記錄進合适的表後,要開始和目标service建立連接配接了。我們可以看到,bindServiceLocked()會嘗試
呼叫起目标service。
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
. . . . . .
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
看到這句if語句,大家應該知道調用bindService()時為什麼常常要加上BIND_AUTO_CREATE了吧:
bindService(intent, conn, Context.BIND_AUTO_CREATE);
這裡面也分為三種情況:
1,目标服務已經啟動了,直接調用c.conn.connected 方法
2,目标服務并沒有啟動,調用requestServiceBindingLocked
3,目标服務所在的程序還未啟動,調用bringUpServiceLocked方法
1.1 connected
connected 方法調用流程圖如下,
c.conn.connected到底是怎麼調用的呢?此時在AMS服務程序中。
C是ConnectionRecord對象, conn 是 IServiceConnection,一看就是跨程序調用。
這時,以下流程都是運作于發起bindService所在的程序中,在LoadedApk的内部類 ServiceDispatcher裡,
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);
}
}
}
ServiceDispatcher的connected方法如下,
public void connected(ComponentName name, IBinder service) {
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0));
} else {
doConnected(name, service);
}
}
RunConnection類定義如下,
private final class RunConnection implements Runnable {
RunConnection(ComponentName name, IBinder service, int command) {
mName = name;
mService = service;
mCommand = command;
}
public void run() {
if (mCommand == 0) {
doConnected(mName, mService);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
final ComponentName mName;
final IBinder mService;
final int mCommand;
}
doConnected方法如下,
public void doConnected(ComponentName name, IBinder service) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
•••
if (service != null) {
mConnection.onServiceConnected(name, service);
}
}
到這兒終于看到調用onServiceConnected方法了,在該方法中,有一個 service參數,這就是被綁定的binder的引用,
通過它就可以進行跨程序調用。
public void doDeath(ComponentName name, IBinder service) {
mConnection.onServiceDisconnected(name);
}