深入理解Activity——Token之旅

yangxi_001發表於2017-02-16
    Token是ActivityRecord的內部靜態類,我們先來看下Token的繼承關係,Token extends IApplicationToken.Stub,從IApplicationToken.Stub類進行繼承,根據Binder的機制可以知道Token是一個匿名Binder實體類,這個匿名Binder實體會傳遞給其他程式,其他程式會拿到Token的代理端。
    我們知道匿名Binder有兩個比較重要的用途,一個是拿到Binder代理端後可跨Binder呼叫實體端的函式介面,另一個作用便是在多個程式中標識同一個物件。往往這兩個作用是同時存在的,比如我們這裡研究的Token就同時存在這兩個作用,但最重要的便是後者,Token標識了一個ActivityRecord物件,即間接標識了一個Activity。
    下面這個圖是Token的傳遞,首先會傳遞到WMS中,接著會傳遞到應用程式ActivityThread中,下面來具體分析這個傳遞流程。


1、Token物件的建立
[java] view plain copy
  1. ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,  
  2.         int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,  
  3.         ActivityInfo aInfo, Configuration _configuration,  
  4.         ActivityRecord _resultTo, String _resultWho, int _reqCode,  
  5.         boolean _componentSpecified, ActivityStackSupervisor supervisor,  
  6.         ActivityContainer container, Bundle options) {  
  7.     service = _service;  
  8.     appToken = new Token(this);  
  9.     ........  
在ActivityRecord的建構函式中建立,標識著當前這個ActivityRecord,即間接代表著一個Activity。

2、AMS呼叫WMS的addAPPToken()介面
在啟動一個Activity時,會呼叫startActivityLocked()來在WMS中新增一個AppWindowToken物件;
[java] view plain copy
  1. final void startActivityLocked(ActivityRecord r, boolean newTask,  
  2.         boolean doResume, boolean keepCurTransition, Bundle options) {  
  3.         ......  
  4.         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,  
  5.                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,  
  6.                 (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,  
  7.                 r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);  
  8.         ......  
  9. }  

[java] view plain copy
  1. public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,  
  2.         int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,  
  3.         int configChanges, boolean voiceInteraction, boolean launchTaskBehind) {  
  4.         ......  
  5.   
  6.     synchronized(mWindowMap) {  
  7.         AppWindowToken atoken = findAppWindowToken(token.asBinder());  
  8.         if (atoken != null) {  
  9.             Slog.w(TAG, "Attempted to add existing app token: " + token);  
  10.             return;  
  11.         }  
  12.         atoken = new AppWindowToken(this, token, voiceInteraction);  
  13.         ......  
  14.         Task task = mTaskIdToTask.get(taskId);  
  15.         if (task == null) {  
  16.             createTask(taskId, stackId, userId, atoken);  
  17.         } else {  
  18.             task.addAppToken(addPos, atoken);  
  19.         }  
  20.         mTokenMap.put(token.asBinder(), atoken);  
  21.         // Application tokens start out hidden.  
  22.         atoken.hidden = true;  
  23.         atoken.hiddenRequested = true;  
  24.         //dump();  
  25.     }  
  26. }  

3、AMS跨Binder呼叫應用程式的scheduleLaunchActivity()將Token傳遞給上層應用程式
[java] view plain copy
  1. final boolean realStartActivityLocked(ActivityRecord r,  
  2.         ProcessRecord app, boolean andResume, boolean checkConfig)  
  3.         throws RemoteException {  
  4.         ......  
  5.         app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,  
  6.                 System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),  
  7.                 r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,  
  8.                 r.icicle, r.persistentState, results, newIntents, !andResume,  
  9.                 mService.isNextTransitionForward(), profilerInfo);  
  10.         ......  
  11. }  
這個是通過呼叫app.thread.scheduleLaunchActivity()完成的,可以知道對端接收到的便是Token的代理物件。
我們來看下ApplicationThread中scheduleLaunchActivity()的實現:
[java] view plain copy
  1. public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,  
  2.         ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,  
  3.         String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,  
  4.         PersistableBundle persistentState, List<ResultInfo> pendingResults,  
  5.         List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,  
  6.         ProfilerInfo profilerInfo) {  
  7.     updateProcessState(procState, false);  
  8.     ActivityClientRecord r = new ActivityClientRecord();  
  9.     r.token = token;  
  10.     r.ident = ident;  
  11.     r.intent = intent;  
  12.     r.referrer = referrer;  
  13.     r.voiceInteractor = voiceInteractor;  
  14.     r.activityInfo = info;  
  15.     r.compatInfo = compatInfo;  
  16.     r.state = state;  
  17.     r.persistentState = persistentState;  
  18.     r.pendingResults = pendingResults;  
  19.     r.pendingIntents = pendingNewIntents;  
  20.     r.startsNotResumed = notResumed;  
  21.     r.isForward = isForward;  
  22.     r.profilerInfo = profilerInfo;  
  23.     updatePendingConfiguration(curConfig);  
  24.     sendMessage(H.LAUNCH_ACTIVITY, r);  
  25. }  
函式中建立一個ActivityClientRecord物件,然後將Token的代理物件儲存在ActivityClientRecord.token中。ActivityClientRecord也代表著一個Activity,不過是在應用程式中,而ActivityRecord是在ActivityManagerService中代表一個Activity。

4、Activity視窗新增
ViewRootImpl.setView()函式中新增Activity視窗時在引數mWindowAttributes中攜帶Token代理物件。
[java] view plain copy
  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {  
  2.     synchronized (this) {  
  3.         if (mView == null) {  
  4.             mView = view;  
  5.             ......  
  6.             mWindowAttributes.copyFrom(attrs);  
  7.             ......  
  8.             try {  
  9.                 ......  
  10.                 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,  
  11.                         getHostVisibility(), mDisplay.getDisplayId(),  
  12.                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);  
  13.             } catch (RemoteException e) {  
  14.                 ......  
  15.             } finally {  
  16.                 ......  
  17.             }  
  18.         ......  
  19.         }  
  20.     }  
  21. }  

[java] view plain copy
  1. public int addWindow(Session session, IWindow client, int seq,  
  2.         WindowManager.LayoutParams attrs, int viewVisibility, int displayId,  
  3.         Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {  
  4.         ......  
  5.         boolean addToken = false;  
  6.         WindowToken token = mTokenMap.get(attrs.token);  
  7.         ......  
  8.         win = new WindowState(this, session, client, token,  
  9.                 attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);  
  10.   
  11.         mWindowMap.put(client.asBinder(), win);  
  12.         ......  
  13. }  

根據Binder機制可以知道從上層應用傳遞過來的Token代理物件會轉換成SystemServer程式中的Token本地物件,後者與第2步中從Token物件是同一個物件,所以上面呼叫mTokenMap.get(attrs.token)時便能返回正確返回一個WindowToken(這個WindowToken其實是一個APPWindowToken),這樣新增的視窗也就跟Activity關聯上了。

轉自:http://blog.csdn.net/guoqifa29/article/details/46819377#comments

相關文章