接着上一篇,本文就解決《篇Binder詳解》末尾拋出的問題,也就是如下的問題:
我們客戶端(即MainActivity)接受遠程對象是在自己重寫的ServiceConnection的onServiceConnected()方法中接收的,那麼系統是何時對ServiceConnection的onServiceConnect()方法進行回調的呢?這一切要從我們調用bindService()說起。
bindService()的發源地
我們知道,在Activity要啓動Service有兩種方式,一種就是startService(),還有一種就是bindService(),兩者的使用場景不一樣,啓動的參數也不同,前者是當Service和當前組件在同一個進程時使用,後者是當Service和當前組件不在同一個進程時使用,而且bindService()還需要傳遞一個ServiceConnection實例。雖然我們在Activity中可以直接使用bindService(),但是bindService()並不是Activity的方法,而是Context的方法,我們都知道,四大組件的超類都是Context,只不過各組件的繼承鏈不一樣,比如Activity的繼承鏈是:Activity→ContextThemeWrapper→ContextWrapper→Context。此外Context中的bindService()只是一個abstract方法,真正的實現是在ContextImpl中,而且ContextWrap也把bindService()給包裝了一下,包裝的目的就是去調用ContextImpl中真正實現了的bindService(),所以我們在Activity中調用bindService()的時候最後實際上是調用了ContextImpl的bindService()。但是在Activity的繼承鏈中我們似乎並沒有發現ContextImpl的影子?確實,Activity並沒有直接繼承ContextImpl,ContextImpl是一個隱藏的類。那麼Activity是如何去使用到ContextImpl的bindService的()?我們可以看到在ContextWrap中有一個變量叫做mBase,此mBase就是一個Context實例,確切說是一個ContextImpl實例,而且ContextWrapper的bindService()就是一行代碼:mBase.bindService()來實現調用ContextImpl的bindService()的。那麼mBase他是在什麼時候被賦值的?那就是在Activity被創建時調用其attach()的時候傳遞過來的,當ActivityThread在創建Activity的時候就會調用ContextImpl的createActivityContext()方法new一個ContextImpl實例,並調用Activity的atatch()方法爲mBase賦值。說了這麼多,其實就是想說兩點,第一bindService()是ContextImpl中的方法,第二既然Activity沒有直接實現ContextImpl,那爲什麼我們可以直接調用bindService()。
bindService()所做的一切
現在我們開始對bindService()進行分析,通過上面的介紹,那我們的分析自然是從ContextImpl的bindService()開始了,上代碼:
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
//這個方法僅僅是打印日誌用於警告,忽略
warnIfCallingFromSystemProcess();
//調用了bindServiceCommon()看下面
return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}
/**
* 啓動遠程Service
* @param service Intent指定要啓動的Service
* @param conn ServiceConnection,用於Service連接後就通過這個對象來回調其方法返回遠程的Binder對象
* @param flags
* @param user
* @return
*/
private boolean bindServiceCommon(Intent service, ServiceConnection conn,
int flags, UserHandle user) {
IServiceConnection sd;
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
//mPackageInfo是一個LoadApk實例,此方法用於返回一個IServiceConnection實例也就是InnerConnection類型的對象
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
//獲取令牌,表示要bindService的身份,這裏就是用來表明Activity的身份
IBinder token = getActivityToken();
if (token == null
&& (flags & BIND_AUTO_CREATE) == 0
&& mPackageInfo != null
&& mPackageInfo.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
service.prepareToLeaveProcess();
//AMS調用bindService()
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException("Not allowed to bind to service "
+ service);
}
return res != 0;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
可以看到在bindServiceCommon()方法中,會首先通過mPackageInfo.getServiceDispatcher()獲取一個IServiceConnection實例,這個mPackageInfo是一個LoadApk實例,我們去看看getServiceDispatcher()是個什麼方法:
/**
* 獲取一個IServiceConnection實例,
* @param c
* @param context
* @param handler
* @param flags
* @return
*/
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
//ServiceDispatcher是LoadedApk的內部類,在此方法中就會創建一個InnerConnection實例
sd = new ServiceDispatcher(c, context, handler, flags);
//如果本地還沒有此鏈接就創建新的連接並保存到數組
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
//返回InnerConnection實例mIServiceConnection
return sd.getIServiceConnection();
}
}
首先會從本地map中讀取是否已經存在ServiceDispatcher實例,如果沒有就會去創建一個,注意ServiceDispatcher是LoadedApk的內部類,而ServiceDispatcher裏還有一個內部類InnerConnection,這個InnerConnection是很重要的,它就是繼承了IServiceConnection.Stub的類。在ServiceDispatcher的構造方法中就會根據我們bindService()時傳入的ServiceConnection實例創建InnerConnection,最後在方法的結尾,調用ServiceDispatcher實例返回一個IServiceConnection實例。這裏再強調一下,IServiceConnection就相當於上一篇文章的CustomBinder,而InnerConnection相當於TestService的MyBinder!我們可以看到IServiceConnection的結構和CustomBinder的結構一模樣的。而且在IServiceConnection.Stub中有一個抽象的connected()方法,此方法就是在InnerConnection中實現的,而InnerConnection中的connected()方法最終會調用到我們在調用bindService()時傳入的ServiceConnection的onServiceConnected()方法,從而告知客戶端服務已連接,並且返回一個遠程對象。那麼InnerConnection的connected()方法何時被調用的呢?
上面講了一大堆獲取了IServiceConnection的過程,我們回到ContextImpl的bindServiceCommon()方法中去,在獲取IServiceConnection實例後,就又通過跨進程通信讓ActivityManagerService調用bindService()方法,也就是通過下面一段代碼:
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
關於他是如何跨進程通信到ActivityManagerService的,請看另一篇文章http://blog.csdn.net/u012481172/article/details/49658633; 好,接下來進入到AMS的bindService(),看下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");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
synchronized(this) {
//這個mServices是一個ActivityService實例
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
忽略掉不重要的信息,直接看方法的最後一行,發現是調用了ActivityService的bindServiceLocked()方法,好我們去看看這個方法: int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags,
String callingPackage, int userId) throws TransactionTooLargeException {
//根據調用者來獲取正在運行的當前app的所有進程信息
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
//如果沒有找到進程記錄就說明進程跪了,拋出異常
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when binding service " + service);
}
ActivityRecord activity = null;
if (token != null) {
//根據token獲取ActivityRecord,也就是獲取調用bindService()的那個Activity
//這一步就是從任務棧中去查找Activity
activity = ActivityRecord.isInStackLocked(token);
//如果沒有找到就會綁定失敗
if (activity == null) {
return 0;
}
}
int clientLabel = 0;
PendingIntent clientIntent = null;
final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
//這裏根據Intent獲取Service
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
ServiceRecord s = res.record;
final long origId = Binder.clearCallingIdentity();
try {
if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
}
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
}
mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
s.appInfo.uid, s.name, s.processName);
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
//創建一個Connection記錄,Service的連接
//ConnectionRecord的解釋是:Description of a single binding to a service
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
b.connections.add(c);
if (activity != null) {
if (activity.connections == null) {
activity.connections = new HashSet<ConnectionRecord>();
}
//把ConnectionRecord保存到ActivityRecord中也就是雲行的Activity,
activity.connections.add(c);
}
if (s.app != null && b.intent.received) {
try {
//這裏開始就是調用了InnerConnection的connected()方法,這種調用是跨進程的,即它調用時會首先調用IServiceConnection.Stub.Proxy的connected()方法
//這個流程在上一篇文章已經講過了,不再贅述,總之,它最後就調用了我們的MainActivity中定義的MyServiceConnection的onServiceConnected()方法,這個b.intent.binder就是遠程的對象即IServiceConnection對象。
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
其實這個方法很長,已經省了不少代碼了,可以看到它裏面調用了c.conn.connected(s.name,b.intent.binder),這就是回調了MainActivity中我們定義的MyServiceConnection的onServiceConnected()方法了。至此,整個binderService()的過程已經講完了,雖然分析得還有點粗糙,今後會慢慢研究慢慢體會了~~