初步理解 Window 体系

Android 中的 Window,是一个比较抽象的概念,总有一种说不清道不明的感觉,但是又非常重要。
Activity 是四大组件之一,可以说是我们学习 Android 接触的第一个知识点,它也承担着 UI 和交互的控制逻辑,是我们开发应用经常用到的;View 也是我们经常用到的,从 XML 布局编写到 Activity & Fragment 中 UI 展示变化等,我们也很熟悉;相比之下,Window 我们在平时的开发中用到的不多,相对来说比较陌生。这篇文章就从熟悉的 Activity 入手,理解 Window。

一. Window 的创建 & 添加

Window 和 Activity 有着紧密的联系,一个 Activity 拥有至少一个 Window 对象,在创建 Activity 的时候,也会创建一个 Window 对象,并且 Activity 会实现 Window 的一些回调接口。

在上篇文章 深入理解 Activity 的生命周期 中,我们从源码的角度分析了 Activity 的生命周期,在 Activity 对象创建完成之后,调用的 Activity 第一个方法并不是 onCreate 相关的方法,而是 attach 方法,在 attach 方法中我们创建了 Activity 对应的 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 处创建了 Window 对象,Window 是一个抽象类,其具体的实现类是 PhoneWindow 类

  2. 在代码 2 处设置了 Window 的 Callback 回调,用户的触摸 & 键盘等输入事件就是通过此接口回调到 Activity 中的,Window#Callback 回调源码如下

    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 对象设置了 WindowManager 对象, WindowManager 是一个 Interface,其实现类是 WindowManagerImpl 类,我们来看下,最后通过 WindowManagerImpl 的 createLocalWindowManager 方法创建了一个新的 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 在整个应用进程中是以单例存在的,也就是说在整个应用进程中,所有的 Window 和 WindowManagerImpl 对象都对应着这个 WindowManagerGlobal 对象,其源码如下,在其中有几个变量非常重要

    • ArrayList<View> mViews:存储所有 Window 所对应的 View
    • ArrayList<ViewRootImpl>:存储所有 Window 所对应的 ViewRootImpl
    • ArrayList<WindowManager.LayoutParams>:存储所有 Window 所对应的 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>();
        
       // 私有构造方法
        private WindowManagerGlobal() {
        }
    
       // 初始化方法
        public static void initialize() {
            getWindowManagerService();
        }
    
        public static WindowManagerGlobal getInstance() {
            synchronized (WindowManagerGlobal.class) {
                if (sDefaultWindowManager == null) {
                    sDefaultWindowManager = new WindowManagerGlobal();
                }
                return sDefaultWindowManager;
            }
        }
    
       // 创建 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 产生了关联
        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) {
            // ......
        }
    }
    

关于 Activity、Window、WindowManager、WindowManagerImpl、WindowManagerGlobal 这几个类的关系可以用如下图来表示可能更清晰一些


image.png

上面我们介绍了 Activity 中的 Window 是如何创建并添加,Activity 和 Window 之间的关系就算是清楚了。接着我们分析 View 是如何添加到 Window 上面的。

二. 创建 PhoneView & DecorView & mContentParent

还是从熟悉的知识入手,我们在创建好一个 Activity 类,在 Activity 的 onCreate 方法中都会调用 setContentView 方法为 Activity 设置 View 的

class MainActivity : Activity() {

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

setContentView 的源码如下,其实是调用了 Window 的 setContentView 方法

public class Activity extends ContextThemeWrapper implements  ... {

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

    public Window getWindow() {
        return mWindow;
    }
}

Window 是一个抽象类,其具体实现类是 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 是否为空,若为空会调用 installDecor() 方法。

    mContentParent 是 PhoneWindow 中非常重要的一个属性,由其英文注释可知:mContentParent 是 Window 放置 View 内容的容器,可能是 mDecor 本身,或者是 mDecor 的一个子 View。

  • 在代码2 处,会通过 mLayoutInflater 将传入的 layoutResID 解析并放入 mContentParent 内
  • 在代码3 处,得到设置的 Callback 回调对象,并调用 onContentChanged(),通知 View 内容发生变化

    这儿其实就是在 Activity#attach 方法中设置的 Callback 回调对象

接下来我们看一下 installDecor() 方法,看看是如何创建 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) {

        // ...... 应用当前的 Theme 

        // 根据主题设置去选择 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 设置为当前 DecorView 的内容
        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.
        // 设置背景和标题
        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;
    }

上面代码就是根据当前的 Theme 设置 Window 的样式,选择合适的 mContentParent 的 layout 布局,并将 mContentParent 加载到 DecorView 中。比较常见的 mContentParent 的 layout 布局是 R.layout.screen_title_icons,其代码位于 /frameworks/base/core/res/res/layout/screen_title.xml,内容如下,其中有个 android:id/title 的 title 和 android:id/content 容器,我们通过 setContentView 设置的 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 方法将其设置为 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 设置为 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 的关系基本上就搞清楚了,如下图所示:


DecorView.png

图片来源:Window源码解析(一):与DecorView的那些事

到这儿其实并没有结束,我们上面都是在 Activity 的 create 阶段分析 PhoneWindow、DecorView、mContentParent 的创建,并没有分析 DecorView 是如何添加到 PhoneWindow 上的,DecorView 添加到 PhoneView 的过程在 Activity 的 resume 过程中完成

三. 向 PhoneView 添加 DecorView

在 ActivityThread 的 handleLaunchActivity 中执行完 create 阶段的方法之后,立即开始调用 handleResumeActivity 方法,开始执行 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,调用 performResumeActivity 间接地调用 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 设置为不可见的
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                // 代码 3,此处很重要,将 WindowManager.LayoutParams.type 属性设置为 TYPE_BASE_APPLICATION = 1,代表此 Window 是一个应用级的窗口,在所有窗口的最低下
                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,在这儿调用 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 处,我们可以清楚的看到调用了 WindowManager 的 addView 方法,那我们接着分析,因为 WindowManager 是一个 Interface,其实现类是 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);
    }

    // ......

 }

其实是调用了 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,则直接调用对应的 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 添加到对应的 List 中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        
            // 调用 ViewRootImpl 的 setView 方法,开始执行 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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容