Window, WindowManager, WindowManagerService 的簡(jiǎn)單梳理(一)
Window, WindowManager, WindowManagerService 的簡(jiǎn)單梳理(二)- Window 的添加過程
相比前面兩篇,Activiy 的 Window 的創(chuàng)建過程要簡(jiǎn)單多了。大致有下面幾個(gè)步驟:
-
PhoneWindow 的創(chuàng)建
在 Activity 啟動(dòng)時(shí),會(huì)通過 ActivityThread 的 performLaunchActivity() 啟動(dòng)對(duì)應(yīng) Activity。其中,就會(huì)調(diào)用 Activity 的 attach()方法。
在 Activity 的 attach() 方法里面,可以看到創(chuàng)建了 PhoneWindow 對(duì)象。當(dāng)然,PhoneWindow 并不是 WindowManagerService 添加的 Window,不是一個(gè)概念。關(guān)于 Window 的概念,見上面兩篇文章。// Activity final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); ... 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()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; }順便一提,Activity 實(shí)現(xiàn)了 Window.Callback 接口。
// Window public interface Callback { public boolean dispatchKeyEvent(KeyEvent event); public boolean dispatchKeyShortcutEvent(KeyEvent event); public boolean dispatchTouchEvent(MotionEvent event); public boolean dispatchTrackballEvent(MotionEvent event); public boolean dispatchGenericMotionEvent(MotionEvent event); public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); public View onCreatePanelView(int featureId); public boolean onCreatePanelMenu(int featureId, Menu menu); public boolean onPreparePanel(int featureId, View view, Menu menu); public boolean onMenuOpened(int featureId, Menu menu); public boolean onMenuItemSelected(int featureId, MenuItem item); public void onWindowAttributesChanged(WindowManager.LayoutParams attrs); public void onContentChanged(); public void onWindowFocusChanged(boolean hasFocus); public void onAttachedToWindow(); public void onDetachedFromWindow(); public void onPanelClosed(int featureId, Menu menu); public boolean onSearchRequested(); public boolean onSearchRequested(SearchEvent searchEvent); public ActionMode onWindowStartingActionMode(ActionMode.Callback callback); public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type); public void onActionModeStarted(ActionMode mode); public void onActionModeFinished(ActionMode mode); default public void onProvideKeyboardShortcuts( List<KeyboardShortcutGroup> data, @Nullable Menu menu, int deviceId) { }; } -
DecorView 的創(chuàng)建
ActivityThread 的 performLaunchActivity() 調(diào)用 attach() 之后,會(huì)調(diào)用到 Activity 的 onCreate。眾所周知,我們是在 onCreate 里面 setContentView 的。// Activity public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }還是進(jìn)入了 PhoneWindow。
然后在 PhoneWindow 的 installDecor 中,用 generateDecor 中創(chuàng)建了 DecorView mDecor;用 generateLayout 創(chuàng)建了 mContentParent。我們?cè)O(shè)置的 Activity 的布局就是最終加入 mContentParent 中進(jìn)行顯示。// PhoneWindow // This is the top-level view of the window, containing the window decor. private DecorView mDecor; // This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. ViewGroup mContentParent; public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { // 內(nèi)部會(huì)初始化 mDecor 和 mContentParent installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { // 將設(shè)置的 Activity 的 Layout 添加到 mContentParent 上面 mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); // 這里的 cb 就是 Activity attach() 的時(shí)候傳入的 Activity 對(duì)象 final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; } private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { // 生成 mDecor mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); } if (mContentParent == null) { // 生成 mContentParent mContentParent = generateLayout(mDecor); // Set up decor part of UI to ignore fitsSystemWindows if appropriate. mDecor.makeOptionalFitsSystemWindows(); final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( R.id.decor_content_parent); ... } } protected DecorView generateDecor(int featureId) { // System process doesn't have application context and in that case we need to directly use // the context we have. Otherwise we want the application context, so we don't cling to the // activity. Context context; ... return new DecorView(context, featureId, this, getAttributes()); } public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. ... ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); ... return contentParent; } -
添加 DecorView 到 WindowManager
《Android 開發(fā)藝術(shù)探索》對(duì)于這個(gè)階段說的很有道理,這個(gè)時(shí)候 DecorView 還沒有被 WindowManager 識(shí)別,所以這個(gè)時(shí)候 Window 還無法提供具體功能,因?yàn)樗€無法接收外界的輸入信息。
DecorView 是在 Activity 的 onResume 階段添加到 WindowManager 的。
具體是在 ActivityThread 的 handleResumeActivity() 方法中觸發(fā)了添加 DecorView 到 WindowManager 的代碼調(diào)用,并且是在調(diào)用了 onResume() 方法之后。
所以我在 Activity 的 onStart() 和 onResume() 中做了測(cè)試:可以 get 到 DecorView,但是 DecorView 的 isAttachedToWindow() 返回的是 false。// ActivityThread final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) { return; } ... // TODO Push resumeArgs into the activity for consideration // performResumeActivity 會(huì)觸發(fā) Activity onResume 的調(diào)用 r = performResumeActivity(token, clearHide, reason); if (r != null) { final Activity a = r.activity; ... // 下面這段代碼就是將 DecorView 添加到 WindowManager if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; ... if (a.mVisibleFromClient && !a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v( TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } ... // 代碼中還有一個(gè)分支會(huì)調(diào)用到 Activity 的 makeVisible(),這里我就直接省略掉了。 // 但是我們可以看一下 Activity 的 makeVisible() 方法的實(shí)現(xiàn) } // Activity void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
可見,Activity 的顯示,最終也是將一個(gè) View 添加到 WindowManager 之中。只不過這里是 DecorView,但是接下來的方法和流程與 Window, WindowManager, WindowManagerService 的簡(jiǎn)單梳理(二)- Window 的添加過程 中介紹的相同。
不僅 Activity 如此,Dialog 和 Toast 最終也是將一個(gè) View 添加到 WindowManager 中,最終實(shí)現(xiàn)自己的顯示窗口的添加,以完成顯示功能。