https://upload-images.jianshu.io/upload_images/5688445-6cf0575bb52ccb45.png
1. ActivityRecord中的token
ActivityRecord在ActivityStackSupervisor的startActivityLocked初始化
final int startActivityLocked(IApplicationThread caller, Intent intent, String resolvedType, ActivityInfo aInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, Bundle options, boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container, TaskRecord inTask) { ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, this, container, options); err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask); }
ActivityRecord
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor, ActivityContainer container, Bundle options) { service = _service; appToken = new Token(this, service); info = aInfo; launchedFromUid = _launchedFromUid; launchedFromPackage = _launchedFromPackage; userId = UserHandle.getUserId(aInfo.applicationInfo.uid); intent = _intent; shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; componentSpecified = _componentSpecified; rootVoiceInteraction = _rootVoiceInteraction; configuration = _configuration; stackConfigOverride = (container != null) ? container.mStack.mOverrideConfig : Configuration.EMPTY; resultTo = _resultTo; resultWho = _resultWho; requestCode = _reqCode; state = ActivityState.INITIALIZING; //... }
Token是ActivityRecord內部類330行
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
static class Token extends IApplicationToken.Stub { private final WeakReference<ActivityRecord> weakActivity; private final ActivityManagerService mService; Token(ActivityRecord activity, ActivityManagerService service) { weakActivity = new WeakReference<>(activity); mService = service; } //... }
啟動一個Activity的時候會為這個Activity生成一個ActivityRecord對象,該對象用於AMS管理跟蹤,而 Token就在這里誕生了。
Token類實現了IApplicationToken.Stub,也就是作為Binder的服務端,那么它自然的接收客戶端的請求,那它主要提供什么樣的服務呢?
看下android/view/IApplicationToken.aidl
interface IApplicationToken { void windowsDrawn(); void windowsVisible(); void windowsGone(); boolean keyDispatchingTimedOut(String reason); long getKeyDispatchingTimeout(); }
可以看出,大部分是WindowManagerService用於通知ActivityManagerService的關於Window的消息,也有key的相關消息
2. WMS中的token
WMS專為Activity實現了一個WindowToken的子類:AppWindowToken
具體位置為ActivityStack.startActivityLocked(),也就是啟動Activity的時候。相關代碼如下:ActivityStack.startActivityLocked()
private final void startActivityLocked(......) { ...... mService.mWindowManager.addAppToken(addPos,r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen); ...... }
startActivityLocked()向WMS聲明r.appToken作為此Activity的Token,這個Token是在ActivityRecord的構造函數中創建的。
將ActivityRecord中的appToken加入到WMS中
public void addAppToken( ) { //生成ActivityRecord在WMS中對應的AppWindowToken,並引用到ActivityRecord中的Token,見p2 atoken = new AppWindowToken(this, token, voiceInteraction); //如果沒有Task, 就創建一個task, 並加入到stack中, //這里的task/stack都是與AMS中task/stack就一一對應的。 見p3 Task task = mTaskIdToTask.get(taskId); if (task == null) { task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds, config); } //將AppWindowToken加入到task中管理起來 task.addAppToken(addPos, atoken, taskResizeMode, homeTask); mTokenMap.put(token.asBinder(), atoken); //加入到mTokenMap中, 見p4 }
AppWindowToken(WindowManagerService _service, IApplicationToken _token, boolean _voiceInteraction) { //將token的binder對象給AppWindowToken的父類WindowToken引用 super(_service, _token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; //引用到ActivityRecord中的Token voiceInteraction = _voiceInteraction; mInputApplicationHandle = new InputApplicationHandle(this); mAppAnimator = new AppWindowAnimator(this); }
wtoken.appToken.windowsDrawn();
wtoken.appToken.windowsVisible();
appWindowToken.appToken.keyDispatchingTimedOut(reason);
作用
WindowManagerService中AppWindowToken保存着ActivityManagerService Binder對象,用來向AMS傳遞Window和按鍵的一些信息.
另外的一個用處是作為 mTokenMap的key
mTokenMap.put(token.asBinder(), atoken);
3. App中的token
attachApplicationLocked realStartActivityLocked app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, );
public final void scheduleLaunchActivity(Intent intent, IBinder token, ) { //生成App中的ActivityClientRecord ActivityClientRecord r = new ActivityClientRecord(); r.token = token; //將AMS中的token保存到 ActivityClientRecord中 見P5 }
將AMS中的token保存到 ActivityClientRecord中
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/app/ActivityThread.java
public final class ActivityThread { //... private ContextImpl mSystemContext; static IPackageManager sPackageManager; final ApplicationThread mAppThread = new ApplicationThread(); final Looper mLooper = Looper.myLooper(); final H mH = new H(); final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); // List of new activities (via ActivityRecord.nextIdle) that should // be reported when next we idle. ActivityClientRecord mNewActivities = null; // Number of activities that are currently visible on-screen. int mNumVisibleActivities = 0; WeakReference<AssistStructure> mLastAssistStructure; final ArrayMap<IBinder, Service> mServices = new ArrayMap<>(); //... }
AMS將ActivityRecord的appToken傳遞給App進程。
//ActivityThread
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { //... //獲取WindowManagerService的Binder引用(proxy端)。 WindowManagerGlobal.initialize(); //會調用Activity的onCreate,onStart,onResotreInstanceState方法 Activity a = performLaunchActivity(r, customIntent); if (a != null) { //... //會調用Activity的onResume方法. handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); //... } }
//ActivityThread private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //通過類加載器創建Activity Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); //... //通過LoadedApk的makeApplication方法來創建Application對象 Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { //... activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window); //... //onCreate mInstrumentation.callActivityOnCreate(activity, r.state); //onStart activity.performStart(); } return activity; }
在Activity的attach()方法里,系統會創建Activity所屬的Window對象並為其設置回調接口,由於Activity實現了Window的Callback接口,因此當Window接收到外界的狀態改變時就會回調Activity的方法。Callback接口中的方法很多,下面舉幾個比較眼熟的方法。
public interface Callback { public boolean dispatchTouchEvent(MotionEvent event); public View onCreatePanelView(int featureId); public boolean onMenuItemSelected(int featureId, MenuItem item); public void onContentChanged(); public void onWindowFocusChanged(boolean hasFocus); public void onAttachedToWindow(); public void onDetachedFromWindow(); }
//Activity final void attach(...) { //綁定上下文 attachBaseContext(context); //創建Window,PhoneWindow是Window的唯一具體實現類 mWindow = new PhoneWindow(this, window);//此處的window==null,但不影響 mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); //... //設置WindowManager mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } //創建完后通過getWindowManager就可以得到WindowManager實例 mWindowManager = mWindow.getWindowManager();//其實它是WindowManagerImpl } @Override public Object getSystemService(@ServiceName @NonNull String name) { //... if (WINDOW_SERVICE.equals(name)) { return mWindowManager; } return super.getSystemService(name); }
等,等一下。看到這里是不是有點懵,Activity的getSystemService根本沒有創建WindowManager,那么mWindow.setWindowManager()設置的豈不是空的WindowManager,那這樣它的下一步mWindowManager = mWindow.getWindowManager()豈不是無線空循環?
因為PhoneWindow中並沒有setWindowManager()方法,所以我們打開Window類看看。
//Window public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } //在此處創建mWindowManager mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); } //在WindowManagerImpl類中 public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow); }
作用
Activity中的token涉及到多個地方,
- ActivityClientRecord
這個類是Activity在ActivityThread中一一對應的,一個APP有多個Activity, 也就是說有多個ActivityClientRecord, 那么當AMS要啟動一個Activity的時候,怎么樣找到APP中正確的那個Activity呢?答案就是通過Token,
如:
public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide, String reason) { ActivityClientRecord r = mActivities.get(token); r.activity.performResume(); }
先通過token找到ActivityClientRecord,然后再通過ActivityClientRecord中的activity就找到了正確的Activity了
- Activity
Activity中Token主要用於在請求AMS服務時用於定位到具體到AMS中正確的ActivityRecord
比如進入PIP模式,通過Token,AMS就可以知道具體是哪個Activity進入PIP,
public void enterPictureInPictureMode() { try { ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken); } catch (RemoteException e) { } }
又比如 startActivityForResult,希望在finish時得到一些結果,那么AMS在finish那個Activity時,會把result傳遞給resultTo(mToken對應的那個Activity),
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
- Window
Window中的Token主要是傳給LayoutParams, 見下面分析
4. WindowManager.LayoutParams里的token
WindowManager.LayoutParams是App中的,但是這里單獨拿出來,是因為WMS會使用到它中的Token
ddView->setView public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { if (parentWindow != null) {//這里parentWindow為PhoneWindow parentWindow.adjustLayoutParamsForSubWindow(wparams); } } void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) { ... } else { //這里為null, 且mContainer也為空,所以將mAppToken直接賦值給wp.token if (wp.token == null) { wp.token = mContainer == null ? mAppToken : mContainer.mAppToken; } } }
由上代碼可知WindowManager.LayoutParams的token為Window中的mAppToken也就是AMS中ActivityRecord中的Token 見p8
作用
WindowManager.LayoutParams中的 token傳遞給WMS,
另外它的大部分作用是一致性判斷
5. WindowState中的token
public int addWindow(WindowManager.LayoutParams attrs) { //attrs.token即是圖中的p8, 這里拿到的token不為null, 具體參考 **WMS中的token** WindowToken token = mTokenMap.get(attrs.token); ... if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW){ //這里atoken即是WindowToken的子類 AppWindowToken, 具體見p2 atoken = token.appWindowToken; } WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); } WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a) { mToken = token; //WindowState引用到WindowToken WindowState appWin = this; while (appWin.isChildWindow()) { //一個Activity上可能有多個窗口,這里找到父窗口 appWin = appWin.mAttachedWindow; } WindowToken appToken = appWin.mToken; // while (appToken.appWindowToken == null) { WindowToken parent = mService.mTokenMap.get(appToken.token); if (parent == null || appToken == parent) { break; } appToken = parent; } mRootToken = appToken; //這里mAppToken就是WindowToken的子類 AppWindowToken. 見P4 mAppToken = appToken.appWindowToken;
從上面代碼看出WindowState也間接有ActivityRecord中的Token的引用。
作用
WMS中的token是通過WindowManager.LayoutParams傳過來的,作用之一是作為
mTokensMap中的key值用來儲存對應的WindowToken
作用之二是通知AMS一些消息,如
mActivityManager.notifyEnterAnimationComplete(atoken.token);
wtoken.appToken.windowsVisible();