Android-Framework: Activity、Window、View三者之间的关系

文章目录

  • Android-Framework: Activity、Window、View三者之间的关系
    • 前言
    • 彻底弄明白setContentView()
      • AppCompat 层实现
        • ensureSubDecor()
        • PhoneWindow 的 getDecorView()实现
          • generateDecor() 创建DecorView
          • generateLayout() 创建ContentParent 对象
        • AppCompat层创建自定义ConentView
        • PhoneWindow 的 setContentView() 实现
        • AppCompat 层 setContentView()
    • View是怎么显示在Activity上的
      • PhoneWindow 什么时候创建的
      • Window什么时候将View添加到Activity上的?
        • WindowManager 的 addView() 实现
        • ViewRootImpl 的 setView()
    • 总结
    • 参考

前言

ActivityView是大家都比较熟悉一个是Android的四大组件、一个是用于展示各种控件的View,相对于前两者 Window 在日常开发中比较陌生,今天我们这篇文章就将这三个问题讲清楚。

源代码版本:

  • Android framework base 版本:为文章发布时 master分支最新代码 GitHub Android framework base
  • AndroidX 版本: 1.0.2 Androidx release

彻底弄明白setContentView()

要想弄清楚 Window对象是什么使用创建的,就要从最简单的代码入手,一步步得往下查看源码才能解开这个迷惑,那我们就开始吧

首先看以下代码:

class MyTestViewActivity : AppCompatActivity(){    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_my_test_view) //打印View所以父ViewLayoutUtils.printAllViewLayout(ll_first_content)           }}

activity_my_test_view.xml 布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:id="@+id/ll_first_content"    android:orientation="vertical"/>

这里我们有一个 继承与 AppCompatActivityMyTestViewActivity 里面只是调用了 setContentView() 将自定义布局文件传入其中,在 onCreate() 我们调用 printAllViewLayout() 将 自定义布局 LinearLayout 的所有父View 打印出来。

printAllViewLayout() 如下:

fun printAllViewLayout(view: View?) {    if (view == null) {        return    }    //打印view所有的parent    var parent = view.parent    while (parent != null) {        Log.d("printAllViewLayout: ","parent : $parent")                parent = parent.parent    }}

上述代码运行之后打印如下信息:

printAllViewLayout:: parent : androidx.appcompat.widget.ContentFrameLayoutprintAllViewLayout:: parent : androidx.appcompat.widget.FitWindowsLinearLayoutprintAllViewLayout:: parent : android.widget.FrameLayoutprintAllViewLayout:: parent : android.widget.LinearLayoutprintAllViewLayout:: parent : com.android.internal.policy.PhoneWindow$DecorViewprintAllViewLayout:: parent : android.view.ViewRootImpl

这里我们看到打印出很多父View 类型、 ContentFrameLayoutFitWindowsLinearLayoutFrameLayoutLinearLayout 以及 DecorViewViewRootImpl 这些都是什么时候添加的呢?又起到什么作用呢?我们带着这两个问题从源码的角度进行一旦究竟吧

首先我们需要从 setContentView() 的具体实现开始分析

AppCompat 层实现

//AppCompatActivity 实现@Overridepublic void setContentView(@LayoutRes int layoutResID) {    getDelegate().setContentView(layoutResID);}//AppCompatDelegate 实现类 AppCompatDelegateImpl @Overridepublic void setContentView(int resId) {//创建DecorView    ensureSubDecor();    //加载自定义布局 并添加到 contentParent 中    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);    contentParent.removeAllViews();    LayoutInflater.from(mContext).inflate(resId, contentParent);    mOriginalWindowCallback.onContentChanged();}

AppCompatActivitysetContentView() 是有其代理类实现,AppCompatDelegateImpl 类中 setContentView() 只做了两件事

  • 执行 ensureSubDecor() 创建自定义ViewGroup对象 :mSubDecor
  • 加载自定义布局并添加到contentParent中 (此时的contentParent 是AppCompatDelegateImpl类中自定义的ViewGroup)

ensureSubDecor()

我们继续往下看 ensureSubDecor() 具体是怎么实现的

private void ensureSubDecor() {    if (!mSubDecorInstalled) {        mSubDecor = createSubDecor();        //省略不重要代码    }}

ensureSubDecor() 中很显然只有一句比较重要的代码 调用了 createSubDecor() 而 mSubDecorInstalled 只是一个标志位用于标识是否已经初始化过了,当执行完 createSubDecor() 会置成 true 因此保证只会初始化一次。

我们接着往下看 createSubDecor() 具体做了什么

private ViewGroup createSubDecor() {    //省略不重要代码...        // 确保window已经加载了 DecorView 如果没有则进行创建并添加到window上    mWindow.getDecorView();        //根据不同的 theme 加载不同的layout 布局文件     final LayoutInflater inflater = LayoutInflater.from(mContext);    ViewGroup subDecor = null;    //存在 windowTitle 情况下加载 带有actionBar 的布局文件    if (!mWindowNoTitle) {        if (mIsFloating) {            // If we're floating, inflate the dialog title decor            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);            // Floating windows can never have an action bar, reset the flags            mHasActionBar = mOverlayActionBar = false;        } else if (mHasActionBar) {            // Now inflate the view using the themed context and set it as the content view            subDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null)}    } else {        if (mOverlayActionMode) {            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);        } else {        //没有windowTitle 默认情况下加载此布局            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);        }    }     // 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中    mWindow.setContentView(subDecor);       // 返回自定义ViewGroup    return subDecor;}

上述createSubDecor() 只做了三件大事,具体是:

  1. 调用WindowgetDecorView()创建DecorView、创建 ContentParent ViewGroup 并加入到DecorView中
  2. AppCompat 层创建自定义样式 ViewGroup并设置相关适配、监听等
  3. 调用WindowsetContentView() 将创建的ViewGroup 添加到 ContentParent 中

接下来我们先一件一件拆开来分析,首先我们来看第一件事 也就是 getDecorView() , Window 是一个抽象类,而 Window 的唯一实现类 是 com.android.internal.policy.PhoneWindow所以我们就看一下 PhoneWindow 是怎么实现的。

PhoneWindow 的 getDecorView()实现

PhoneWindowgetDecorView() 很显然这个方法是个单例的,只会创建一个 DecorView对象,而且具体实现是在 installDecor()

@Overridepublic final @NonNull View getDecorView() {    if (mDecor == null || mForceDecorInstall) {        installDecor();    }    return mDecor;}//安装 DecorView 布局private void installDecor() {    if (mDecor == null) {        //创建 DecorView        mDecor = generateDecor(-1);    }    if (mContentParent == null) {        //创建 ContentParent 布局        mContentParent = generateLayout(mDecor);    }    //省略无关代码}

installDecor() 方法比较长,但实际只干了两件事:

  1. 如果mDecor 为空则调用 generateDecor() 创建 DecorView 对象
  2. 如果 mContentParent 为空则调用 generateLayout() 创建ContentParent 对象
generateDecor() 创建DecorView

generateDecor() 直接创建了一个新的 DecorView对象

protected DecorView generateDecor(int featureId) {    // 省略不重要代码    //创建DecorView 并将当前Window对象传入    return new DecorView(context, featureId, this, getAttributes());}

DecorView 其实就是继承 FrameLayout 的自定义 ViewGroup 、在构造器中调用 setWindow() 将当前 Window进行了保存

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {DecorView(Context context, int featureId, PhoneWindow window,        WindowManager.LayoutParams params) {    super(context);    setWindow(window);    //省略不重要代码}//保存window对象void setWindow(PhoneWindow phoneWindow) {    mWindow = phoneWindow;    Context context = getContext();    if (context instanceof DecorContext) {        DecorContext decorContext = (DecorContext) context;        decorContext.setPhoneWindow(mWindow);    }}
generateLayout() 创建ContentParent 对象

generateLayout() 这个方法比较长、但其作用只有两个:

  1. 应用所有主题设置。从attrs.xml中读取配置并设置、比如 window 的 windowNoTitle、windowFullscreen、windowIsFloating 等都是在这设置的。
  2. 加载 ContentParent 并加入到 DecorView
protected ViewGroup generateLayout(DecorView decor) {    // 1.应用所有主题设置    TypedArray a = getWindowStyle();      mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);    int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)            & (~getForcedWindowFlags());    if (mIsFloating) {        setLayout(WRAP_CONTENT, WRAP_CONTENT);        setFlags(0, flagsToUpdate);    } else {        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);    }    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);    }    //省略部分代码        // 2.加载window ContentView 布局 布局均为 LinearLayout 嵌套 FrameLayoutint layoutResource;//省略layout布局文件匹配代码//加载布局文件mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);return contentParent;}    

上述源码中针对 ContentParent 的布局文件进行了匹配、framework层帮我们定制了很多ContentParent布局文件,但这些布局文件只有两部分组成,Title部分 和 Content 部分、所以ContentView 布局 布局均为 LinearLayout 嵌套 FrameLayout ,默认情况下会加载的是 screen_simple.xml

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>

onResourcesLoaded() 中加载了布局文件,并添加到 DecorView 中成为DecorView中第一个ViewGroup

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {    //加载布局文件    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 {        //将contentView加入到 DecorView中 成为DecorView中第一个ViewGroup        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));    }}

至此 PhonewWindow层的 getDecorView() 实现我们已经分析完毕,我们再回到 AppCompat 层中看一下第二步,看看AppCompat层又做了那些功能。

AppCompat层创建自定义ConentView

还记得上文中 createSubDecor() 方法第二步做了什么吗?

private ViewGroup createSubDecor() {    //省略不重要代码...        // 确保window已经加载了 DecorView 如果没有则进行创建并添加到window上    mWindow.getDecorView();        //根据不同的 theme 加载不同的layout 布局文件     final LayoutInflater inflater = LayoutInflater.from(mContext);    ViewGroup subDecor = null;    //存在 windowTitle 情况下加载 带有actionBar 的布局文件    if (!mWindowNoTitle) {        if (mIsFloating) {            // If we're floating, inflate the dialog title decor            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);            // Floating windows can never have an action bar, reset the flags            mHasActionBar = mOverlayActionBar = false;        } else if (mHasActionBar) {            // Now inflate the view using the themed context and set it as the content view            subDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null)}    } else {        if (mOverlayActionMode) {            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);        } else {        //没有windowTitle 默认情况下加载此布局            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);        }    }     // 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中    mWindow.setContentView(subDecor);       // 返回自定义ViewGroup    return subDecor;}

对 就是 AppCompat 层又增加了一层 ViewGroup 这里我的 Activity 已经去掉了Window Title 所以加载的是 abc_screen_simple.xml 布局文件

abc_screen_simple.xml布局文件:

<?xml version="1.0" encoding="utf-8"?><androidx.appcompat.widget.FitWindowsLinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/action_bar_root"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:fitsSystemWindows="true">    <androidx.appcompat.widget.ViewStubCompat        android:id="@+id/action_mode_bar_stub"        android:inflatedId="@+id/action_mode_bar"        android:layout="@layout/abc_action_mode_bar"        android:layout_width="match_parent"        android:layout_height="wrap_content" />    <include layout="@layout/abc_screen_content_include" />androidx.appcompat.widget.FitWindowsLinearLayout>//abc_screen_content_include 布局文件<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android">    <androidx.appcompat.widget.ContentFrameLayout            android:id="@id/action_bar_activity_content"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:foregroundGravity="fill_horizontal|top"            android:foreground="?android:attr/windowContentOverlay" />merge>

可以看出 AppCompat 层帮我们添加了一层ViewGroup 帮我们实现了其他效果等

PhoneWindow 的 setContentView() 实现

我们前面分析了 AppCompat 层 createSubDecor() 的前两步实现,我们现在看一下,最后一步

// 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中mWindow.setContentView(subDecor);

看一下 PhoneWindow 是怎么实现的

@Overridepublic void setContentView(View view) {    setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {    //创建 mContentParent     if (mContentParent == null) {        installDecor();    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {        mContentParent.removeAllViews();    }    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {        view.setLayoutParams(params);        final Scene newScene = new Scene(mContentParent, view);        transitionTo(newScene);    } else {        //将指定view添加到 mContentParent 中        mContentParent.addView(view, params);    }}

可以看出 当 mContentParent == null 就调用 installDecor() 来创建 DecorViewContentParent 等逻辑 我们前文就已经分析了,最后将指定的View 添加到 ContentParent 中 (这里的view 就是刚才 AppCompat 层 创建的自定义ContentView

至此 PhoneWindowsetContentView() 实现我们已经分析完毕,那么我们重新把视线转移到 AppCompat 层

AppCompat 层 setContentView()

我们重新回到 AppCompat 层 setContentView() 中继续往下分析

//AppCompatDelegate 实现类 AppCompatDelegateImpl @Overridepublic void setContentView(int resId) {//创建DecorView    ensureSubDecor();    //加载自定义布局 并添加到 contentParent 中    ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);    contentParent.removeAllViews();    LayoutInflater.from(mContext).inflate(resId, contentParent);    mOriginalWindowCallback.onContentChanged();}

这里直接将 resId加载并添加到了contentParent中, mSubDecor 就是 AppCompat 创建的自定义 ViewGroup

至此我们从 setContentView() 出发分析了 setContentView() 底层具体做了哪些操作,创建了哪些 View 分别是怎么实现的,又回到了这里明白了 setContentView() 调用过程,但是 setContentView() 只是创建了 DecorView 及其他ViewGroup 但这些 View 是怎么展示在 Activity上的呢?

View是怎么显示在Activity上的

要想弄清楚 View 是如何展示在 Activity 上的,就首先弄清楚 WindowActivity 之间的关系

PhoneWindow 什么时候创建的

Activity 启动的时候,代码最终会执行 ActivityThreadperformLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  //省略代码...    ContextImpl appContext = createBaseContextForActivity(r);    //创建 activity 实例    Activity activity = null;        java.lang.ClassLoader cl = appContext.getClassLoader();        activity = mInstrumentation.newActivity(                cl, component.getClassName(), r.intent);                       //省略代码...        Application app = r.packageInfo.makeApplication(false, mInstrumentation);        if (activity != null) {            //执行 attach() 并传入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);            //省略代码...            //执行 Activity onCreate()            activity.mCalled = false;            if (r.isPersistable()) {                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);            } else {                mInstrumentation.callActivityOnCreate(activity, r.state);            }      }      //省略代码...    return activity;}

performLaunchActivity()Instrumentation 会通过反射创建 Activity 对象,之后执行其 attach() 并传入Activity 所需参数,Window 对象就是在这个方法中创建的

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, IBinder assistToken) {    attachBaseContext(context);    mFragments.attachHost(null /*parent*/);    //创建PhoneWindow 对象    mWindow = new PhoneWindow(this, window, activityConfigCallback);    mWindow.setWindowControllerCallback(this);    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);    }    //省略代码...        //将系统 WindowManager 传递到 PhoneWindow中    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);    //省略代码...}

可以看到 在 attach() 中创建了 PhoneWindow 之后 调用 setWindowManager() 将系统 WindowManager 传递到 PhoneWindow

而我们知道 WindowManager 只是一个接口,所以真实“干活”的类 其实是 WindowManagerImpl 它实现了 WindowManager ,所以 PhoneWindow 其实持有的 WindowManager 对象是 WindowManagerImpl

//Window 实现public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {    setWindowManager(wm, appToken, appName, false);}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);    }    //创建 WindowManagerImpl 对象    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}//WindowManagerImpl 实现public WindowManagerImpl createLocalWindowManager(Window parentWindow) {    return new WindowManagerImpl(mContext, parentWindow);}

这里我们知道 在创建 Activity 的时候并创建了 PhoneWindow 对象,但是并没有将 View 添加到 Activity上,那 Window 什么时候将 View 添加到 Activity上呢?

Window什么时候将View添加到Activity上的?

其实熟悉Android的开发者可能不陌生,在 Activity 执行完 onCreate() 这个时候界面其实是不可见的,只有等到 onResume() 阶段 PhoneWindow 才将 DecorView 绘制到Activity

ActivityThreadhandleResumeActivity() 才调用 WindowManageraddView() 将 View 添加到 WMS(WindowManagerService)上,DecorView 被渲染绘制到屏幕上显示

@Overridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,        String reason) {    //省略代码...    final Activity a = r.activity;    boolean willBeVisible = !a.mStartedActivity;        if (r.window == null && !a.mFinished && willBeVisible) {        r.window = r.activity.getWindow();        View decor = r.window.getDecorView();        ViewManager wm = a.getWindowManager();               // 如果没有添加过 则将View 添加到 WMS 上        if (a.mVisibleFromClient) {            if (!a.mWindowAdded) {                a.mWindowAdded = true;                wm.addView(decor, l);            }        }    }   //省略代码...}

WindowManager 的 addView() 实现

PhoneWindow 只是负责处理一些应用窗口通用的逻辑(设置标题栏,导航栏等)。但是真正完成把一个 View 作为窗口添加到 WMS 的过程是由 WindowManager来完成的。

上文我们分析过 WindowManager 真实实现类 是 WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)    applyDefaultToken(params);    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);}}

很显然 WindowManagerImpl 将工作交给了一个 WindowManagerGlobal 去做,WindowManagerGlobal 是一个单例对象,看一下 WindowManagerGlobaladdView() 是怎么实现的

public void addView(View view, ViewGroup.LayoutParams params,        Display display, Window parentWindow) {    //省略代码...      ViewRootImpl root;    View panelParentView = null;    synchronized (mLock) {        //省略代码...                //创建 ViewRootImpl 对象        root = new ViewRootImpl(view.getContext(), display);                //调用 ViewRootImpl其 setView来添加view          try {            root.setView(view, wparams, panelParentView);        } catch (RuntimeException e) {            throw e;        }    }}

可见 WindowManagerGlobal 也不是真正实现者,这里 addView() 直接创建出 ViewRootImpl 对象并调用 setView()View 添加到 WMS 中。

ViewRootImpl 的 setView()

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {    synchronized (this) {          //省略代码...                  //开始View的绘制流程            requestLayout();                       try {                mOrigWindowType = mWindowAttributes.type;                mAttachInfo.mRecomputeGlobalAttributes = true;                collectViewAttributes();                //将View 添加到 WMS 中                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                        getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,                        mTempInsets);                            } catch (RemoteException e) {                throw new RuntimeException("Adding window failed", e);            } finally {                if (restore) {                    attrs.restore();                }            }            //省略代码...                       // 设置输入管道            CharSequence counterSuffix = attrs.getTitle();            mSyntheticInputStage = new SyntheticInputStage();            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,                    "aq:native-post-ime:" + counterSuffix);            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);            InputStage imeStage = new ImeInputStage(earlyPostImeStage,                    "aq:ime:" + counterSuffix);            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,                    "aq:native-pre-ime:" + counterSuffix);            mFirstInputStage = nativePreImeStage;            mFirstPostImeInputStage = earlyPostImeStage;    }}

ViewRootImplsetView() 是一个比较重要的方法,但是这个方法比较长,我们挑重点代码进行分析,其实做了一下事情:

  • 调用 requestLayout() 开启View的绘制流程。 调用此方法后 ViewRootImpl 所关联的 View 也执行 measure - layout - draw 操作,确保在 View 被添加到 Window 上显示到屏幕之前,已经完成测量和绘制操作。
  • 调用 IWindowSessionaddToDisplay() 使用 Binder 通讯方式将 View 传递给 WMS
  • 设置输入事件管道来接受事件输入

我们这里只重点分析第二步,将 Window 显示在 屏幕上,这里我们看到 mWindowSession.addToDisplay()mWindowSession 对象也是 WindowManagerGlobal 中单例实现,初始化代码如下:

@UnsupportedAppUsagepublic static IWindowSession getWindowSession() {    synchronized (WindowManagerGlobal.class) {        if (sWindowSession == null) {            try {                // Emulate the legacy behavior.  The global instance of InputMethodManager                @UnsupportedAppUsage                InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();                IWindowManager windowManager = getWindowManagerService();                sWindowSession = windowManager.openSession(                        new IWindowSessionCallback.Stub() {                            @Override                            public void onAnimatorScaleChanged(float scale) {                                ValueAnimator.setDurationScale(scale);                            }                        });            } catch (RemoteException e) {                throw e.rethrowFromSystemServer();            }        }        return sWindowSession;    }}

返回的 IWindowSession 其实是一个 aidl 定义接口,其实现类 是 System 进程的 Session (源码路径:com.android.server.wm.Session.java)

// IWindowSession.aidl interface IWindowSession {    int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,            in int viewVisibility, in int layerStackId, out Rect outFrame,            out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,            out InsetsState insetsState);}           //com.android.server.wm.Session.java 部分源码class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {    final WindowManagerService mService;    final IWindowSessionCallback mCallback;@Overridepublic int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,        int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,        Rect outStableInsets, Rect outOutsets,        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,        InsetsState outInsetsState) {    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,            outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,            outInsetsState);}}            

Session 中的 mService 就是 WMS。至此,Window 已经成功的被传递给了 WMS。剩下的工作就全部转移到系统进程中的 WMS 来完成最终的添加操作。

总结

至此,我们从源码的角度分析了 通过 setContentView() 的流程,分析了 ActivityWindowView 之间的关系。整个过程 Activity 表面上参与度比较低,大部分 View 的添加操作都由 Window 中实现。而 Activity 就相当于一个管理类,通过它能够更简单的实现 WindowView 的操作逻辑。

根据它们之间的关系可以绘制出以下示例图:

最后再简单列一下整个流程需要注意的点:

  1. 一个 Activity 中有一个 Window,也就是 PhoneWindow 对象,在 PhoneWindow 中会创建一个 DecorView,在 调用setContentView() 的时候会将布局文件添加到此 DecorView 中。
  2. 一个应用进程中只有一个 WindowManagerGlobal 对象
  3. 每一个 PhoneWindow 对应一个 ViewRootImpl 对象
  4. WindowMangerGlobal 通过调用 ViewRootImplsetView(),完成 Window 的添加过程,这一步是使用 Binder 进行通讯的。
  5. ViewRootImplsetView() 除了完成对Window的添加还完成了 View 渲染(requestLayout())以及接收触屏事件。

好了,从本文的学习 你是否明白了文章开头在 ActivityonCreate() 中我们打印出来的那些父View,是什么时候添加进去的呢?

参考

Android FrameWork Base 源代码:https://github.com/aosp-mirror/platform_frameworks_base

AndroidX release 源代码:https://android.googlesource.com/platform/frameworks/support/+refs

https://blog.csdn.net/lu1024188315/article/details/74911179

更多相关文章

  1. android中下拉框控件——Spinner
  2. Android(安卓)网络下载文件 图片 httpurl
  3. Android音乐播放器-图片素材准备及布局
  4. delphi xe5 android 开发数据访问server端(一)
  5. Android重写ViewGroup实现卡片布局(三)
  6. Android(安卓)录音实现方法、仿微信语音、麦克风录音、发送语音
  7. Android(安卓)studio中给Button添加点击事件的4种方法
  8. Android的Adapter
  9. Android之Fragments

随机推荐

  1. android图片压缩处理,并保存
  2. Android(安卓)GPS Location with Listene
  3. 安装 ADT 插件
  4. Ubuntu10.04安装JDK5
  5. Android权限 (代码区2)
  6. Android(安卓)NeedProxy
  7. Flutter Weekly Issue 61
  8. Install Android(安卓)NDK No.1
  9. android GridView条目点击变色
  10. 设置网络首选项