• View绘制流程1-View与Window的关系


    前言:

    这几天享学课堂alvin老师讲WMS机制,有幸听了,感触颇深,所以回头写了这篇文章,进行知识点的记录和回顾。

    想要搞清楚View的完整的绘制流程,我认为主要分为三大块需要掌握。

    第一块,最终呈现给用户看的其实是Window,那么Window与View的关系是怎样的?主要是搞清楚Window,DecorView,ViewRootImpl,WindowManager的关系。

    第二块,了解了各个组件之间的关系,那么我们就可以开始了解一次完整的绘制是执行了怎样的一个流程。绘制流程是如何执行到我们常说的measure,layout,draw的流程上的。

    第三块,draw的流程走完了。产生了各种绘制UI的指令,那么这些指令是如何形成数据,发给SurfaceFlinger,最终发送到硬件层进行展示的。

    所以围绕着这是三块,写了三篇文章来进行详细的讲解。第一块和第二块主要是java层的讲解,第三块主要是JNI层的讲解。

    一.几个比较重要的角色

    开始流程介绍之前,我们先介绍一些比较重要的角色:

    1.DecorView:这是最顶层的View,继承自ViewGroup。我们经常设置的setContentView等等,都是放到DecorView中的。

    2.ViewRootImpl:最顶层的ViewParent,但是并不是一个View。它属于一个管理者,维护DecorView和Window的关系。绘制流程的控制等等,都是由其来维护的。

    3.PhoneWindow:Window翻译过来是窗口,它在安卓中的概念就是锁定屏幕上的一块区域进行显示。而PhoneWindow则是用来装载和显示DecorView的,我们activity中setConentView方法最终也会交给PhoneWindow的setConentView来实现。

    4.WindowManagerImpl:WindowManagerGlobal的代理类,基本上功能都是交由WindowManagerGlobal处理。

    5.WindowManagerGlobal:视图的管理装载类。一个应用中会有很多activity,其实每个activity都会对应一个DecorView,而这些DecorView都会保存在WindowManagerGlobal中。

    6.IWindowSession:WindowManageService在客户端的Binder代理类。客户端最终的绘制操作,需要通过binder传递给WindowManageService,然后由其在传递给SurfaceFlinger去完成最终的合成。

    二.PhoneWindow,DecorView,ViewRootImpl何时创建的?

    2.1 PhoneWindow何时创建?

    PhoneWindow是在ActivityThread执行performLaunchActivity方法的时候去创建的。Activity一定要关联window才能显示,所以在activity之初就创建了其所对应的Window。

    1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    2. ...
    3. //反射创建activity
    4. ContextImpl appContext = createBaseContextForActivity(r);
    5. Activity activity = null;
    6. try {
    7. java.lang.ClassLoader cl = appContext.getClassLoader();
    8. activity = mInstrumentation.newActivity(
    9. cl, component.getClassName(), r.intent);
    10. StrictMode.incrementExpectedActivityCount(activity.getClass());
    11. r.intent.setExtrasClassLoader(cl);
    12. r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
    13. appContext.getAttributionSource());
    14. if (r.state != null) {
    15. r.state.setClassLoader(cl);
    16. }
    17. } catch (Exception e) {
    18. if (!mInstrumentation.onException(activity, e)) {
    19. throw new RuntimeException(
    20. "Unable to instantiate activity " + component
    21. + ": " + e.toString(), e);
    22. }
    23. }
    24. ...
    25. //activity进行绑定
    26. activity.attach(appContext, this, getInstrumentation(), r.token,
    27. r.ident, app, r.intent, r.activityInfo, title, r.parent,
    28. r.embeddedID, r.lastNonConfigurationInstances, config,
    29. r.referrer, r.voiceInteractor, window, r.configCallback,
    30. r.assistToken, r.shareableActivityToken);
    31. ...
    32. return activity;
    33. }

    Activity.attch方法:

    1. final void attach(Context context, ActivityThread aThread,
    2. Instrumentation instr, IBinder token, int ident,
    3. Application application, Intent intent, ActivityInfo info,
    4. CharSequence title, Activity parent, String id,
    5. NonConfigurationInstances lastNonConfigurationInstances,
    6. Configuration config, String referrer, IVoiceInteractor voiceInteractor,
    7. Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
    8. IBinder shareableActivityToken) {
    9. attachBaseContext(context);
    10. mFragments.attachHost(null /*parent*/);
    11. mWindow = new PhoneWindow(this, window, activityConfigCallback);
    12. mWindow.setWindowControllerCallback(mWindowControllerCallback);
    13. mWindow.setCallback(this);
    14. mWindow.setOnWindowDismissedCallback(this);
    15. mWindow.getLayoutInflater().setPrivateFactory(this);
    16. ...
    17. }

    2.2 DecorView何时创建?

    DecorView是在Activity的onCreate方法时创建的,具体是在setContentView的时候。这时候contentView需要找一个父容器进行装载,则恰好是需要创建的DecorView的时候。

    Activity的setContentView方法最终会交给PhoneWindow的setContentView方法进行处理。则此时会判断如果mContentParent为空,则进行DecorView的创建。

    1. if (mContentParent == null) {
    2. installDecor();
    3. }

    installDecor方法中,如果DecorView不存在,则会去创建。

    1. if (mDecor == null) {
    2. mDecor = generateDecor(-1);
    3. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    4. ...
    5. }

    创建完成后,再去判断mContentParent是否存在,如果不存在则去创建contentParent,并且把其加入到DecorView中。

    1. if (mContentParent == null) {
    2. mContentParent = generateLayout(mDecor);
    3. }

    generateLayout方法中,根据layoutResource反射去创建ViewGroup,然后把其加入到DecorView中。

    1. protected ViewGroup generateLayout(DecorView decor) {
    2. ...
    3. mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    4. ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    5. return contentParent;
    6. }

    这里生成root对象,其实就是我们常用的contentView,然后加入到DecorView中。DecorView虽然是ViewGroup,但其child也只会有这一个对象。

    1. void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    2. ...
    3. final View root = inflater.inflate(layoutResource, null);
    4. ...
    5. addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    6. ...
    7. }

    这里稍微扩展一下,我们经常设置的各种主题颜色,其实也是在这个时候进行解析处理的。主题样式其实就是作用于Window上面的。

    2.3 ViewRootImpl何时创建?

    上面两个对象的创建都是在需要其的时候创建的,那么ViewRootImpl自然也不例外。界面开始渲染其实是在onResume的时候,那么ViewRootImpl的创建就是在onResume的时候。

    ActivityThread执行到handleResumeActivity方法的时候,会进行判断,如果ActivityClientRecord中还未绑定window并且没有执行finish方法,则会向WindowManager注册decorView。

    这里我们看到判断了finish状态,也就是说如果在onReusme之前如果执行了finish方法,是不会走创建流程的,也不会创建ViewRootImpl,从而避免创建不需要的对象,这就体现了需要时才去创建的合理性。

    1. if (r.window == null && !a.mFinished && willBeVisible) {
    2. r.window = r.activity.getWindow();
    3. View decor = r.window.getDecorView();
    4. decor.setVisibility(View.INVISIBLE);
    5. ViewManager wm = a.getWindowManager();
    6. WindowManager.LayoutParams l = r.window.getAttributes();
    7. a.mDecor = decor;
    8. l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
    9. l.softInputMode |= forwardBit;
    10. if (r.mPreserveWindow) {
    11. a.mWindowAdded = true;
    12. r.mPreserveWindow = false;
    13. // Normally the ViewRoot sets up callbacks with the Activity
    14. // in addView->ViewRootImpl#setView. If we are instead reusing
    15. // the decor view we have to notify the view root that the
    16. // callbacks may have changed.
    17. ViewRootImpl impl = decor.getViewRootImpl();
    18. if (impl != null) {
    19. impl.notifyChildRebuilt();
    20. }
    21. }
    22. //向WindowManager注册DecorView
    23. wm.addView(decor, l);
    24. }

    WindowManager的最终实现类是WindowManagerGlobal,其addView方法如下:

    1. public void addView(View view, ViewGroup.LayoutParams params,
    2. Display display, Window parentWindow, int userId) {
    3. ...
    4. root = new ViewRootImpl(view.getContext(), display);
    5. view.setLayoutParams(wparams);
    6. mViews.add(view);
    7. mRoots.add(root);
    8. mParams.add(wparams);
    9. ...
    10. //ViewRootImpl绑定DecorView
    11. root.setView(view, wparams, panelParentView, userId);
    12. }
    13. }

    我们可以看到,会先创建ViewRootImpl,然后把DecorView和root分别加入到集合当中,最终在把DecorView和ViewRootImpl进行绑定。

    所以我们可以得出这样一个结论,DecorView和ViewRootImpl是一一对应的。

    这里继续扩展一下,问一个曾经的百度面试题,如果我在ActivityB中,想拿到上一个页面ActivityA的View该怎么做呢?聪明的你看到mViews估计就已经知道答案了。

    三.后续流程

    等几个重要的对象都已经创建并且完成绑定后,则后续就可以执行渲染的流程了。

    这个流程也是从onResume中的方法开始的,具体后续流程,我们在下一章里面去讲。

  • 相关阅读:
    【华为机试真题 JAVA】高矮个子排队-100
    (附源码)计算机毕业设计SSM晋中学院教室管理系统
    大学生阅读小说网页设计模板代码 柏书旧书网带登录表单 注册表单小说书籍网页作业成品 学校书籍网页制作模板 学生简单书籍阅读网站设计成品
    elasticsearch7 实战应用
    MATLAB库函数resample(重采样函数)的C语言实现【姊妹篇2纯C语言实现】
    Map+函数式接口如何“更完美”的解决 if-else的问题
    函数中使用sizeof(arr) / sizeof(arr[0])求数组长度不正确的原因
    Git 查看当前分支是基于哪个分支拉取(源头分支)
    UML之类图
    企业内业务系统与Activiti流程引擎的结合(十二)
  • 原文地址:https://blog.csdn.net/AA5279AA/article/details/125989959