初步理解 Window 體系

Android 中的 Window,是一個比較抽象的概念,總有一種說不清道不明的感覺胧卤,但是又非常重要枝誊。
Activity 是四大組件之一侧啼,可以說是我們學(xué)習(xí) Android 接觸的第一個知識點皮壁,它也承擔(dān)著 UI 和交互的控制邏輯,是我們開發(fā)應(yīng)用經(jīng)常用到的虑瀑;View 也是我們經(jīng)常用到的,從 XML 布局編寫到 Activity & Fragment 中 UI 展示變化等痛侍,我們也很熟悉主届;相比之下君丁,Window 我們在平時的開發(fā)中用到的不多橡庞,相對來說比較陌生印蔗。這篇文章就從熟悉的 Activity 入手毙死,理解 Window。

一. Window 的創(chuàng)建 & 添加

Window 和 Activity 有著緊密的聯(lián)系喻鳄,一個 Activity 擁有至少一個 Window 對象扼倘,在創(chuàng)建 Activity 的時候,也會創(chuàng)建一個 Window 對象除呵,并且 Activity 會實現(xiàn) Window 的一些回調(diào)接口。

在上篇文章 深入理解 Activity 的生命周期 中颜曾,我們從源碼的角度分析了 Activity 的生命周期纠拔,在 Activity 對象創(chuàng)建完成之后,調(diào)用的 Activity 第一個方法并不是 onCreate 相關(guān)的方法泛豪,而是 attach 方法稠诲,在 attach 方法中我們創(chuàng)建了 Activity 對應(yīng)的 Window 對象,代碼如下

public class Activity extends ContextThemeWrapper implements ...... {

    private Window mWindow;
    private WindowManager mWindowManager;

    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, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        // 代碼 1
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        // 代碼 2
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }

        // 代碼 3
        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;

        mWindow.setColorMode(info.colorMode);
    }

}
  1. 在代碼 1 處創(chuàng)建了 Window 對象诡曙,Window 是一個抽象類臀叙,其具體的實現(xiàn)類是 PhoneWindow 類

  2. 在代碼 2 處設(shè)置了 Window 的 Callback 回調(diào),用戶的觸摸 & 鍵盤等輸入事件就是通過此接口回調(diào)到 Activity 中的价卤,Window#Callback 回調(diào)源碼如下

    public abstract class Window { 
    
        /**
         * API from a Window back to its caller.  This allows the client to
         * intercept key dispatching, panels and menus, etc.
         */
        public interface Callback {
            /**
             * Called to process key events.  At the very least your
             * implementation must call
             * {@link android.view.Window#superDispatchKeyEvent} to do the
             * standard key processing.
             *
             * @param event The key event.
             *
             * @return boolean Return true if this event was consumed.
             */
            public boolean dispatchKeyEvent(KeyEvent event);
    
            /**
             * Called to process a key shortcut event.
             * At the very least your implementation must call
             * {@link android.view.Window#superDispatchKeyShortcutEvent} to do the
             * standard key shortcut processing.
             *
             * @param event The key shortcut event.
             * @return True if this event was consumed.
             */
            public boolean dispatchKeyShortcutEvent(KeyEvent event);
    
            /**
             * Called to process touch screen events.  At the very least your
             * implementation must call
             * {@link android.view.Window#superDispatchTouchEvent} to do the
             * standard touch screen processing.
             *
             * @param event The touch screen event.
             *
             * @return boolean Return true if this event was consumed.
             */
            public boolean dispatchTouchEvent(MotionEvent event);
    
            // ......
        }
    }
    
  3. 在代碼 3 處為 Window 對象設(shè)置了 WindowManager 對象劝萤, WindowManager 是一個 Interface,其實現(xiàn)類是 WindowManagerImpl 類慎璧,我們來看下床嫌,最后通過 WindowManagerImpl 的 createLocalWindowManager 方法創(chuàng)建了一個新的 WindowManagerImpl 方法,并且將 Window 自己傳入 WindowManagerImpl 中胸私,在 WindowManagerImpl 的 mParentWindow 持有 Window 的對象

    public abstract class Window {
    
        /**
         * Set the window manager for use by this Window to, for example,
         * display panels.  This is <em>not</em> used for displaying the
         * Window itself -- that must be done by the client.
         *
         * @param wm The window manager for adding new windows.
         */
        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 = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        }
    
    }
    
    public final class WindowManagerImpl implements WindowManager {
        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
        private final Context mContext;
        private final Window mParentWindow;
    
        private IBinder mDefaultToken;
    
        public WindowManagerImpl(Context context) {
            this(context, null);
        }
    
        private WindowManagerImpl(Context context, Window parentWindow) {
            mContext = context;
            mParentWindow = parentWindow;
        }
    
        public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
            return new WindowManagerImpl(mContext, parentWindow);
        }
    
        public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
            return new WindowManagerImpl(displayContext, mParentWindow);
        }
    
        // ......
    
        @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
    
        @Override
        public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.updateViewLayout(view, params);
        }
    
        // ......
    
        @Override
        public void removeView(View view) {
            mGlobal.removeView(view, false);
        }
    
        @Override
        public void removeViewImmediate(View view) {
            mGlobal.removeView(view, true);
        }
    
        // ......
    }
    
  4. 在 WindowManagerImpl 類中持有一個 WindowManagerGlobal 對象厌处,WindowManagerGlobal 在整個應(yīng)用進(jìn)程中是以單例存在的,也就是說在整個應(yīng)用進(jìn)程中岁疼,所有的 Window 和 WindowManagerImpl 對象都對應(yīng)著這個 WindowManagerGlobal 對象阔涉,其源碼如下,在其中有幾個變量非常重要

    • ArrayList<View> mViews:存儲所有 Window 所對應(yīng)的 View
    • ArrayList<ViewRootImpl>:存儲所有 Window 所對應(yīng)的 ViewRootImpl
    • ArrayList<WindowManager.LayoutParams>:存儲所有 Window 所對應(yīng)的 WindowManager.LayoutParams
    • ArraySet<View>:存儲所有即將被移除的 View
    public final class WindowManagerGlobal {
        
        // WindowManagerGlobal 單例對象 
        private static WindowManagerGlobal sDefaultWindowManager;
        // IWindowManager & IWindowSession 用于和 WindowManagerService 通過 Binder 通信
        private static IWindowManager sWindowManagerService;
        private static IWindowSession sWindowSession;
    
        private final Object mLock = new Object();
    
        private final ArrayList<View> mViews = new ArrayList<View>();
        private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
        private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
        private final ArraySet<View> mDyingViews = new ArraySet<View>();
        
       // 私有構(gòu)造方法
        private WindowManagerGlobal() {
        }
    
       // 初始化方法
        public static void initialize() {
            getWindowManagerService();
        }
    
        public static WindowManagerGlobal getInstance() {
            synchronized (WindowManagerGlobal.class) {
                if (sDefaultWindowManager == null) {
                    sDefaultWindowManager = new WindowManagerGlobal();
                }
                return sDefaultWindowManager;
            }
        }
    
       // 創(chuàng)建 IWindowManager 對象
        public static IWindowManager getWindowManagerService() {
            synchronized (WindowManagerGlobal.class) {
                if (sWindowManagerService == null) {
                    sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                    try {
                        if (sWindowManagerService != null) {
                            ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                        }
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
                return sWindowManagerService;
            }
        }
    
        // 非常重要的方法,向 Window 中添加 View洒敏,在這里 Window 和 View 產(chǎn)生了關(guān)聯(lián)
        public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            // ......
        }
    
        // 更新 Window 中的 View
        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
            // ......
        }
    
        // 從 Window 中移除 View
        public void removeView(View view, boolean immediate) {
            // ......
        }
    }
    

關(guān)于 Activity龄恋、Window、WindowManager凶伙、WindowManagerImpl郭毕、WindowManagerGlobal 這幾個類的關(guān)系可以用如下圖來表示可能更清晰一些


image.png

上面我們介紹了 Activity 中的 Window 是如何創(chuàng)建并添加,Activity 和 Window 之間的關(guān)系就算是清楚了函荣。接著我們分析 View 是如何添加到 Window 上面的显押。

二. 創(chuàng)建 PhoneView & DecorView & mContentParent

還是從熟悉的知識入手,我們在創(chuàng)建好一個 Activity 類傻挂,在 Activity 的 onCreate 方法中都會調(diào)用 setContentView 方法為 Activity 設(shè)置 View 的

class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

setContentView 的源碼如下乘碑,其實是調(diào)用了 Window 的 setContentView 方法

public class Activity extends ContextThemeWrapper implements  ... {

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

    public Window getWindow() {
        return mWindow;
    }
}

Window 是一個抽象類,其具體實現(xiàn)類是 PhoneWindow 類

public class PhoneWindow extends Window implements MenuBuilder.Callback { 

    // 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;

    @Override
    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
        // 代碼 1
        // before this happens.
        if (mContentParent == null) {
            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 {
           // 代碼 2
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        // 代碼 3
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
  • 在代碼1 處金拒,首先會判斷 mContentParent 是否為空兽肤,若為空會調(diào)用 installDecor() 方法。

    mContentParent 是 PhoneWindow 中非常重要的一個屬性绪抛,由其英文注釋可知:mContentParent 是 Window 放置 View 內(nèi)容的容器资铡,可能是 mDecor 本身,或者是 mDecor 的一個子 View幢码。

  • 在代碼2 處笤休,會通過 mLayoutInflater 將傳入的 layoutResID 解析并放入 mContentParent 內(nèi)
  • 在代碼3 處,得到設(shè)置的 Callback 回調(diào)對象症副,并調(diào)用 onContentChanged()店雅,通知 View 內(nèi)容發(fā)生變化

    這兒其實就是在 Activity#attach 方法中設(shè)置的 Callback 回調(diào)對象

接下來我們看一下 installDecor() 方法,看看是如何創(chuàng)建 mDecormContentParent 對象的

    private void installDecor() {
        mForceDecorInstall = false;
        // 代碼 1
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        // 代碼 2
        if (mContentParent == null) {
            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);

            // ......
        }
    }
  • 在代碼 1 處通過 generateDecor(-1) 方法生成一個 mDecor 的實例對象
  • 在代碼 2 處通過 generateLayout(mDecor) 方法生成 mContentParent 實例對象

generateDecor(int featureId) 方法源碼如下贞铣,最后一行生成了一個 DecorView 的實例對象闹啦,DecorView 就是一個 FrameLayout 的子類,這里不做過多的分析

    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;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }
}

接下來看一下 generateLayout(mDecor) 源碼咕娄,通過 mDecor 生成一個 mContentParent 對象


    protected ViewGroup generateLayout(DecorView decor) {

        // ...... 應(yīng)用當(dāng)前的 Theme 

        // 根據(jù)主題設(shè)置去選擇 layoutResource
        // 這個 layoutResource 就是 DecorView 的子 View 的布局
        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                // 比較常見的是這個 layout 布局 
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();
        // 通過 DecorView 的 onResourcesLoaded 方法將 layoutResource 設(shè)置為當(dāng)前 DecorView 的內(nèi)容
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        // 通過 android.R.id.content id 得到 contentParent, 它是 setContentView 的父視圖
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks(contentParent);
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        // 設(shè)置背景和標(biāo)題
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            mDecor.setWindowBackground(background);

            final Drawable frame;
            if (mFrameResource != 0) {
                frame = getContext().getDrawable(mFrameResource);
            } else {
                frame = null;
            }
            mDecor.setWindowFrame(frame);

            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);

            if (mTitle != null) {
                setTitle(mTitle);
            }

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

上面代碼就是根據(jù)當(dāng)前的 Theme 設(shè)置 Window 的樣式亥揖,選擇合適的 mContentParent 的 layout 布局,并將 mContentParent 加載到 DecorView 中圣勒。比較常見的 mContentParent 的 layout 布局是 R.layout.screen_title_icons,其代碼位于 /frameworks/base/core/res/res/layout/screen_title.xml摧扇,內(nèi)容如下圣贸,其中有個 android:id/title 的 title 和 android:id/content 容器,我們通過 setContentView 設(shè)置的 View 的 parentView 就是這個 android:id/content

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:fitsSystemWindows="true">
    <!-- Popout bar for action modes -->
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <TextView android:id="@android:id/title" 
            style="?android:attr/windowTitleStyle"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

generateLayout(DecorView decor) 方法中得到了 DecorView 的直接子 View 的 layout 布局 id扛稽,然后通過 DecorView#onResourcesLoaded 方法將其設(shè)置為 DecorView 的直接子 View吁峻,我們來分析下 onResourcesLoaded 源碼

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { 
    ViewGroup mContentRoot;

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        mStackId = getStackId();

        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                    getCurrentColor(mNavigationColorViewState));
        }

        mDecorCaptionView = createDecorCaptionView(inflater);
        // 通過 LayoutInflater 加載 layoutResource 布局,通過 root 引用
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {
            
            // 將 root View 設(shè)置為 DecorView 的第 0 個 View
            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        // 將 root 賦給 mContentRoot 引用
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }
}

看到這里,Activity用含、PhoneWindow矮慕、DecorView、mContentParent 的關(guān)系基本上就搞清楚了啄骇,如下圖所示:


DecorView.png

圖片來源:Window源碼解析(一):與DecorView的那些事

到這兒其實并沒有結(jié)束痴鳄,我們上面都是在 Activity 的 create 階段分析 PhoneWindow、DecorView缸夹、mContentParent 的創(chuàng)建痪寻,并沒有分析 DecorView 是如何添加到 PhoneWindow 上的,DecorView 添加到 PhoneView 的過程在 Activity 的 resume 過程中完成

三. 向 PhoneView 添加 DecorView

在 ActivityThread 的 handleLaunchActivity 中執(zhí)行完 create 階段的方法之后虽惭,立即開始調(diào)用 handleResumeActivity 方法橡类,開始執(zhí)行 resume 過程,handleResumeActivity 方法如下


    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;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // 代碼 1芽唇,調(diào)用 performResumeActivity 間接地調(diào)用 Activity 的 onResume 方法
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManager.getService().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                // 代碼 2顾画,在添加 DecorView 到 Window 之前,將 DecorView 設(shè)置為不可見的
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                // 代碼 3匆笤,此處很重要研侣,將 WindowManager.LayoutParams.type 屬性設(shè)置為 TYPE_BASE_APPLICATION = 1,代表此 Window 是一個應(yīng)用級的窗口疚膊,在所有窗口的最低下
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        // 代碼 4义辕,在這兒調(diào)用 WindowManager 的 addView 方法,將 DecorView 添加到 Window 中
                        wm.addView(decor, l);
                    } else {
                        // The activity will get a callback for this {@link LayoutParams} change
                        // earlier. However, at that time the decor will not be set (this is set
                        // in this method), so no action will be taken. This call ensures the
                        // callback occurs with the decor set.
                        a.onWindowAttributesChanged(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;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r, false /* force */);

           // ......

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManager.getService()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

在代碼 4 處寓盗,我們可以清楚的看到調(diào)用了 WindowManager 的 addView 方法灌砖,那我們接著分析,因為 WindowManager 是一個 Interface傀蚌,其實現(xiàn)類是 WindowManagerImpl基显,我們來到 WindowManagerImpl 方法中,如下:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

    // ......

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    // ......

 }

其實是調(diào)用了 WindowManagerGlobal 的 addView 方法善炫,

public final class WindowManagerGlobal {


    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        // ......

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            
            // ......
            // 代碼 1撩幽,從 mViews 中找到此 DecorView 的 index 的索引
            int index = findViewLocked(view, false);
            if (index >= 0) {
                // 若在 mDyingViews 中存在此 DecorView,則直接調(diào)用對應(yīng)的 ViewRootImpl.doDei() 方法
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
             
            // 代碼 2箩艺,新建一個 ViewRootImpl 對象
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            
            // 代碼 3窜醉,將 DecorView、ViewRootImple艺谆、WindowManager.LayoutParams 添加到對應(yīng)的 List 中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        
            // 調(diào)用 ViewRootImpl 的 setView 方法榨惰,開始執(zhí)行 DecorView 的繪制過程
            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

    private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市静汤,隨后出現(xiàn)的幾起案子琅催,更是在濱河造成了極大的恐慌居凶,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藤抡,死亡現(xiàn)場離奇詭異侠碧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)缠黍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門弄兜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嫁佳,你說我怎么就攤上這事挨队。” “怎么了蒿往?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵盛垦,是天一觀的道長。 經(jīng)常有香客問我瓤漏,道長腾夯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任蔬充,我火速辦了婚禮蝶俱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饥漫。我一直安慰自己榨呆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布庸队。 她就那樣靜靜地躺著积蜻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彻消。 梳的紋絲不亂的頭發(fā)上竿拆,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音宾尚,去河邊找鬼丙笋。 笑死,一個胖子當(dāng)著我的面吹牛煌贴,可吹牛的內(nèi)容都是我干的御板。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼牛郑,長吁一口氣:“原來是場噩夢啊……” “哼稳吮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起井濒,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤灶似,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瑞你,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酪惭,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年者甲,在試婚紗的時候發(fā)現(xiàn)自己被綠了春感。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡虏缸,死狀恐怖鲫懒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刽辙,我是刑警寧澤窥岩,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站宰缤,受9級特大地震影響颂翼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慨灭,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一朦乏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧氧骤,春花似錦呻疹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惶翻,卻和暖如春姑蓝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吕粗。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工纺荧, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颅筋。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓宙暇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親议泵。 傳聞我的和親對象是個殘疾皇子占贫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容