WindowManagerService 阅读笔记 1(基于Android 11)

1,337 阅读10分钟

参考文章

  1. 初步理解 Window 体系 - 简书
  2. AMS——Activity管理之Activity的启动销毁流程 - 简书

什么是 Window

window 顾名思义就是窗口的意思,在 android 最终图形的绘制都是通过window进行绘制的。WindowView的管理者,不论是 Activity,Toast,Dialog 最终都是将其View附加在Window上的。

Window的创建过程

Activity 中 Window创建过程

Activity的生命周期之中,onCreate 方法并不是第一个被调用的。实际上第一个被调用的方法是 attach方法。 在ActivityThread.java#performLaunchActivity方法中,acitivty实例被创建后,先调用attach方法再调用了onCreate方法.

ActivityThread.java中代码如下(注意:以下代码有删减,只保留了大致流程代码)

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        ...
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity =mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        Configuration config = new Configuration(mCompatConfiguration);

        appContext.setOuterContext(activity);
        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, r.configCallback,
                r.assistToken);

        ...
        if (r.isPersistable()) {
            //系统关机后保存数据的能力,暂时不太理解其作用,
            //需要在清单文件中对Acitivty配置persistableMode属性
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {//一般都是走到这里
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        ...
        return activity;
    }

callActivityOnCreate方法内部流程为 callActivityOnCreate->Activity#performCreate->Activity#onCreate 接下来,让我们看下 attach方法

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*/);
		// activityConfigCallback 用于监听配置改变,该参数最终被传入ViewRootImpl#setActivityConfigCallback 
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        // 在状态栏,画中画等模式改变时调用
        mWindow.setWindowControllerCallback(this); 
        // this 传入的是Window.Callback 
        // 界面有关的callback调用,window属性更改,contentView改变等
        // 以及事件的分发 
        mWindow.setCallback(this);
        //窗口消失时回调 onWindowDismissed,在Activity中会调用finish
        mWindow.setOnWindowDismissedCallback(this);
        // 设置 根View的构造方法,Activity#onCreateView
        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);
        }
        // 获取当前UI线程
        mUiThread = Thread.currentThread();
        // 当前主线程 一般而言 mUiThread = mMainThread
        mMainThread = aThread;
        // 构造
        mInstrumentation = instr;
        /*
        token是一个binder代理,一个Token类,继承自IApplicationToken.Stub ,用于标识activity
        token只有一个方法getName
        */
        mToken = token;
        // ident值为0
        mIdent = ident;
        // 当前应用对象
        mApplication = application;
        // 传入的intent
        mIntent = intent;
        // 调用者 信息
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        //默认标题
        mTitle = title;
        // 基本为null(Fragment出现后,这个属性就基本被弃用了)
        mParent = parent;
        // 不知道作用
        mEmbeddedID = id;
        // 不知
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        // mVoiceInteractor 语音交互
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
        // setWindowManager 将WindowManger传入
        // WindowManager是个interface,其实现类是 WindowManagerImp
        // 而WindowMangerImpl是个代理类,最终是通过WindowMnagerGlobal进行各种窗口的操作。而WindowManagerGlobal是一个进程内的单例类。
        // FLAG_HARDWARE_ACCELERATED 判断Activity是否开启了硬件加速
        // 关于加速,可以参考文章
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        //如果有mParent,则当前 Activity窗口的DecorView不作为顶级窗口,而是根据其父容器决定如果自身如何显示
        //否则,Activity的的DecorView将作为顶层Window
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        // 获取得到 Acitivty对应的`WindowManagerImpl`对象
        mWindowManager = mWindow.getWindowManager();
        // 当前的一些配置,如 横竖平方向等
        mCurrentConfig = config;
        //设置位图模式,具体可参考 https://developer.android.google.cn/training/wide-color-gamut?hl=zh-cn
        mWindow.setColorMode(info.colorMode);
		
        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
        enableAutofillCompatibilityIfNeeded();
    }

首先可见在attach方法中直接new 一个 PhoneWindow 实例 ,

mWindow = new PhoneWindow(this, window, activityConfigCallback);

具体PhoneWindow创建如下

/**
     * Constructor for main window of an activity.
     */
    public PhoneWindow(Context context, Window preservedWindow,
            ActivityConfigCallback activityConfigCallback) {
        this(context);
        // Only main activity windows use decor context, all the other windows depend on whatever
        // context that was given to them.
        mUseDecorContext = true;
        if (preservedWindow != null) {
            mDecor = (DecorView) preservedWindow.getDecorView();
            mElevation = preservedWindow.getElevation();
            mLoadElevation = false;
            mForceDecorInstall = true;
            // If we're preserving window, carry over the app token from the preserved
            // window, as we'll be skipping the addView in handleResumeActivity(), and
            // the token will not be updated as for a new window.
            getAttributes().token = preservedWindow.getAttributes().token;
        }
        // Even though the device doesn't support picture-in-picture mode,
        // an user can force using it through developer options.
        boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
        mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_PICTURE_IN_PICTURE);
        mActivityConfigCallback = activityConfigCallback;
    }

mWindow.setCallback(this)处设置了WindowCallback 回调,其大多与输入触摸等事件相关,具体回调方法如下:

  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);
      ......

setWindowManager 处有两点需要分析

  1. getSystemService获取的对象
  2. setWindowManager的内部操作
 mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

1. getSystemService获取的对象

context 传入的是 一个ContextImple对象,其 getSystemService 内部实现如下

 @Override
    public Object getSystemService(String name) {
        if (vmIncorrectContextUseEnabled()) {
          ...
        }
        return SystemServiceRegistry.getSystemService(this, name);
    }

而在 SystemServiceRegistry类中,静态构建了一个 WindowManagerImpl对象,因此显然 Activity#attach方法中setWindowManager 传入的是一个WindowManagerImpl对象。

static{
 registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
            @Override
            public WindowManager createService(ContextImpl ctx) {
                return new WindowManagerImpl(ctx);
            }});
}
/**
     * Statically registers a system service with the context.
     * This method must be called during static initialization only.
     */
    private static <T> void registerService(@NonNull String serviceName,
            @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
        SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
    }

2.setWindowManager 的内部操作

setWindowManager方法,将 创建一个WindowManagerImpl对象用于管理Window,且将传入的WindowManagerImpl实例作为 其 mParentWindow属性

显然 getSystemService中得到的WindowManagerImpl实例是所有其他的AcitivtyWindowManagerImpl对象的mParentWindow属性。

Window.java 代码如下

 public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

WindowManagerImpl.java代码如下:

public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @VisibleForTesting
    public 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);
    }
    ...
}

以上代码说明了ActivityWindow 的创建过程,接下来分析 View 添加到Window的过程

View添加到Window的过程

Activity 中 View添加到 Window 的过程

Window添加完成后,基本就会立即开始调用onCreate方法,在onCreate中进行View的添加。 在文章开头介绍的第一段 performLaunchActivity 方法中代码可见,在调用attach方法后,之后就会调用mInstrumentation.callActivityOnCreate方法,在调用Activity#performCreate方法,走到Activity#onCreate方法

Instrumentation.java#callActivityOnCreate代码如下:

public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
}

Activity.java#performCreate代码如下:

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        ...
        if (persistentState != null) {
            onCreate(icicle, persistentState);
        } else {
            onCreate(icicle);//走到onCreate处
        }
        ...
    }

DecorView 的创建

首先Activity#onCreate 中调用了setContentView方法

Activity#setContentView方法实际还是调用了Window#setContentView方法

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

getWindow().setContentView这段调用内容如下:

PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    private DecorView mDecor;
    ViewGroup mContentParent;
    ...
    @Override
    public void setContentView(int layoutResID) {
        //代码1
        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();
        final Callback cb = getCallback();
        //代码 3
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
}

  • 代码1处首先会判断 mContentParent 是否为 null,如为null,则调用

mContentParent 是PhoneWindow中一个重要属性。 mContentParent 是 Window 放置 View 内容的容器,可能是 mDecor 本身,或者是 mDecor 的一个子 View。( mDecor 是顶级View,DecorView继承了FragmentLayout类)

  • 代码2处会通过 mLayoutInflater 将传入的 layoutResID 解析并放入 mContentParent 内。

  • 代码3处得到设置的 Callback 回调对象,并调用 onContentChanged(),通知 View 内容发生变化。

此处Callback 对象就是在 Activity#attach 方法中设置的 Callback 回调对象. onContentChanged onContentChanged()是Activity中的一个回调方法 当Activity的布局改动时,即setContentView()或者addContentView()方法执行完毕时就会调用该方法, 例如,Activity中各种View的findViewById()方法都可以放到该方法中。当然,一般而言,大多数开发者都是在 onCreate 方法中,执行 setContentView 后 获取对象实例。

接下来看下 installDecor方法

installDecor 实现

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.makeFrameworkOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = 
            		(DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);

            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
                mDecorContentParent.setWindowCallback(getCallback());
                ...
            } else {
                mTitleView = findViewById(R.id.title);
                ...
            }

     	  ...
        }
    }
  • 代码1 处,调用generateDecor方法生成一个 mDecor对象,DecorView的构造方法中将 调用 setWindow方法,将PhoneWindow对象传入。

generateDecor 方法

  • 代码2 处 将调用generateLayout生成 mContentParent 对象 generateDecor 代码如下,没有太多需要介绍的
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, this);
                if (mTheme != -1) {
                    context.setTheme(mTheme);//设置主题, AndroidManifest 设置的主题在 ActivityThread#performLaunchActivity中赋值
                }
            }
        } else {//
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

generateLayout 代码如下

protected ViewGroup generateLayout(DecorView decor) {

        // 根据当前的theme 来设置
        TypedArray a = getWindowStyle();
        //判断是否是悬浮窗
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        ...
        // 根据 theme 判断是否添加 actionBar、title
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        ...


        WindowManager.LayoutParams params = getAttributes();
        //是否绘制 状态栏、导航栏 背景
        if (!mIsFloating) {
            if (!targetPreL && a.getBoolean(
                    R.styleable.Window_windowDrawsSystemBarBackgrounds,
                    false)) {
                setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                        FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
            }
            if (mDecor.mForceWindowDrawsBarBackgrounds) {
                params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
            }
        }
        //是否将statusbar 和navigationBar绘制为明亮主题
        if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
        if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
            decor.setSystemUiVisibility(
                    decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
        }
        //当前模式是否为刘海屏模式,获得刘海屏的模式
        if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {
            int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);
            if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
                    || mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
                throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "
                        + a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));
            }
            params.layoutInDisplayCutoutMode = mode;
        }

        ...
        //当前是否是输入法模式
        if (!hasSoftInputMode()) {
            params.softInputMode = a.getInt(
                    R.styleable.Window_windowSoftInputMode,
                    params.softInputMode);
        }

        //窗口动画
        if (params.windowAnimations == 0) {
            params.windowAnimations = a.getResourceId(
                    R.styleable.Window_windowAnimationStyle, 0);
        }

        // The rest are only done if this window is not embedded; otherwise,
        // the values are inherited from our container.
        if (getContainer() == null) {
            if (mBackgroundDrawable == null) {

                if (mFrameResource == 0) {
                    mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
                }

                if (a.hasValue(R.styleable.Window_windowBackground)) {
                    mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);
                }
            }
            if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {
                mBackgroundFallbackDrawable =
                        a.getDrawable(R.styleable.Window_windowBackgroundFallback);
            }
            if (mLoadElevation) {
                mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
            }
            mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
            mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
        }

        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // 代码1
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        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 {
                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();
        // 代码2 
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        // ID_ANDROID_CONTENT = com.android.internal.R.id.content
        // findViewById 是从 mDecor 中查找View对象
        //代码3
        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);
            }
        }

        // 如果 getContainer == null ,说明其是顶层View,一般Activity的 Window对应的都是顶层Window.
        // 设置 背景色以及标题颜色
        if (getContainer() == null) {
            mDecor.setWindowBackground(mBackgroundDrawable);

            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;
    }
  • 代码1处,根据feature以及 window 的类型 来确定引入的 mContentParent 布局。一般全屏显示所布局为 R.layout.screen_simpleR.layout.screen_simple 中有个android:id="@android:id/content"FrameLayout 布局(不管layoutResource对应的布局文件是那个,其都有android:id="@android:id/content"FrameLayout)。
  • 代码2处,调用DecorView#onResourcesLoaded,传入 layout 的 id,解析生成一个DecorView的View对象属性mContentRoot.
  • 代码3处,findViewById 的实现在Window.java中,其最终调用mDecorfindViewById方法,即搜索 mDecor下的对应id的View.而ID_ANDROID_CONTENT的值为com.android.internal.R.id.content.显然,findViewById获得的ViewGroup对象就是 mDecor下的mContentRoot下的 id为@android:id/contentFrameLayout对象。

接下来,就是代码分析了。 首先是R.layout.screen_simple(/frameworks/base/core/res/res/layout/screen_simple.xml)布局代码,如下所示,这个没有可多介绍的。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true"
     android:orientation="vertical">
     <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:id="@android:id/content"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:foregroundInsidePadding="false"
          android:foregroundGravity="fill_horizontal|top"
          android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

接下来看下 DecorView#onResourcesLoaded代码。

class DecorView{
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
            if (mBackdropFrameRenderer != null) {
                ...
            }
            //代码1
            mDecorCaptionView = createDecorCaptionView(inflater);
            //代码2处
            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 {
                //代码3
                // Put it below the color views.
                addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mContentRoot = (ViewGroup) root;
            initializeElevation();
        }
}
  • 代码1 处创建了一个 mDecorCaptionView对象,createDecorCaptionView 根据其英文注释是 自由浮动重叠窗口需要一个标题。(原文:Free floating overlapping windows require a caption.) 在 ActivitysetContentView流程中,mDecorCaptionView == null,因此代码会走到代码3处
  • 代码2 根据layoutResource,解析资源文件,生成对应的View,即 R.layout.screen_simple(或其他)解析出来的View
  • 代码3 将布局文件解析出来的root对象,添加为DecorView的子View

再来看下PhoneView#generateLayout方法中 findViewById方法实现

class Window{
    @Nullable
    public View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }
}

显然 findViewById 就是从 mDecor下寻找idR.id.contentViewGroup对象。即PhoneViewmContentParent对象 就是 DecorView下的mContentRoot的下的一个 idcontent的 子ViewGroup.

再回到PhoneView#setContentView方法,看代码2处代码

mLayoutInflater.inflate(layoutResID, mContentParent);

layoutResIDActivity#setContentView传入的layout 文件id值。显然就是将Activity#setContentView传入的布局作为 mContentParent的子View

看了这么多,我们对这些ActivityPhoneWindowDecorView 能够汇总一个包涵关系了。

image.png

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

但是以上只是DecorView的创建过程,此时View还没被添加到Window 上去显示!!!

向 PhoneView 添加 DecorView

ActivityThreadhandleLaunchActivity 中执行完 create 阶段的方法之后,立即开始调用 handleResumeActivity 方法,开始执行 resume 过程,handleResumeActivity 方法如下

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...
        //代码 1 间接调用 Activity的onResume 
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        ...
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            //代码2 在 DecorView 添加 到 Window 之前,设置为不可见
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            // 代码3 设置 Window的类型,TYPE_BASE_APPLICATION 代表此 Window是一个应用级窗口,在该应用所有窗口最底下(如Dialog窗口就在 该窗口上面)
            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 将decor 添加到 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;
        }

        ...
    }

代码4处调用了WindowManageraddView方法。WindowManager的实现类是WindowManagerImpl,而WindowManagerImpl#addView 调用的是 WindowManagerGlobal addView方法。 具体WindowManagerGlobal#addView可以参考这篇文章Android 9和9之后系统 在onKeyDown中Dialog.show问题分析(以andoird 11 和android 9代码作为分析)