Window, WindowManager, WindowManagerService 的簡(jiǎn)單梳理(三)- Activiy 的 Window 的創(chuàng)建過程

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)自己的顯示窗口的添加,以完成顯示功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容