• framework源码读后感


    View部分

    1.ViewParent

      今天查看了ViewGroup,ViewRootImpl和ViewParent的部分源代码,前面的两个类都实现了ViewParent接口。ViewGroup是一个抽象类,所以它无需实现ViewParent接口里面的方法,既然这样那么ViewGroup的子类应该会实现ViewParent里面的方法,就以requestLayout()方法为例找一找好了,于是我去找了FrameLayout类,搜索,竟然没有
    requestLayout()方法的实现,这个可是具体类啊,竟然没有实现这个方法,不科学啊,不科学,然而这个类里面有一处对requestLayout()方法的调用,既然都没实现,那怎么可以调用呢?在ide里面鼠标点击过去一看,你猜怎么着?竟然跳转到了View类里面的requestLayout()方法,这个方法不是实现了某接口的方法,然而它却是ViewParent中方法
    的一个同名方法,还能有这样的操作???
    在这里插入图片描述

      (1)关于ViewParent的理解
    这是一个接口,ViewGroup和ViewRootImpl类都实现了这个接口,ViewRootImpl类直接实现了requestLayout()方法,会对整个view树进行遍历执行measure,layout,draw操作。

      sdk里面的DecorView类的ViewParent变量指向的正是ViewRootImpl,requestLayout()方法层层向上(往view树根的方向)调用,当到达DecorView的requestLayout()方法调用的时候就会触发ViewRootImpl的requestLayout()方法,从而触发measure,layout,draw的三大流程。
      (2)WindowManager.AddView
    在ActivityThread类,进行到activity生命周期的onResume()方法的时候,会有addView的
    操作,这个调用流程大致是WindowManager=>WindowManagerImpl=>WindowManagerGlobal=>ViewRootImpl。
    在ViewRootImpl中的setView方法里面,DecorView的ViewParent会被赋值为ViewRootImpl。

    ViewRootImpl.java
    // setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
    //        int userId){
    //   ...
    //   view.assignParent(this);
    //   ...
    // }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      (3)这里面有个疑问就是,每一层普通View的ViewParent是在何时赋值的呢?
      activity#setContentView(int)=>PhoneWindow#setContentView
    LayoutInflater#inflate=>LayoutInflater#rInflate,此方法中有个while循环,在调用viewGroup.addView()方法的时候,在addView方法的内部继续找ViewGroup#addViewInner找到了蛛丝马迹,ViewGroup在调用addViewInner方法的时候,在方法的内部对子view的ViewParent属性进行了赋值。

    private void addViewInner(View child, int index, LayoutParams params,
                boolean preventRequestLayout) {
        ...
        if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " +
                    "You must call removeView() on the child's parent first.");
        }
        ...
        // tell our children
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这段源码的追溯实在坎坷,如果能调试一下就更有把握确定这个调用顺序了。

    2.Window

      Window是一个抽象类,它唯一的实现类是PhoneWindow类,它有一个WindowManager类型的属性,何时赋值的呢?
      这个属性的赋值是在ActivityThread中的handleLaunchActivity(…)方法=>performLaunchActivity(…)方法里面在反射实例化完成Activity和Application实例后,调用Activity的attach(…)方法的时候,在attach方法中创建了PhoneWindow对象,并调用了Window的setWindowManager方法对WindowManager属性进行了赋值。

    3.View.invalidate

      invalidate()方法只会触发三大流程中的draw流程,measure和layout并不会触发,那么invalidate()调用后框架层是如何执行的呢?看了一篇博客,说类似requestLayout()方法层层向viewParent方向调用,最终会调用到ViewRootImpl.java中的方法里面去,这个时候就会执行一个scheduleTraversal()方法,进而触发draw流程。捋了一遍源码还没看太明白。还得再捋一遍。
      View调用自己的ViewParent属性的如下方法ViewParent invalidateChildInParent(int[] location, Rect r)继续返回ViewParent(子类)的ViewParent属性,这个方法在ViewGroup.java和ViewRootImpl.java中进行了实现,这样就会一直向view树的树根DecorView的ViewParent(ViewRootImpl),进而调用其ViewParent invalidateChildInParent(int[] location, Rect r)方法,从而触发三大流程的draw流程,就实现了view重新绘制的操作,基本捋明了。

    x.sdk源码中的位运算

    //00100000
    //|
    //00010000
    //00110000
    //0x30
    
    取反和与运算
    //~
    //00110000
    //11001111
    //&
    //11111111
    //11001111
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    	static final int PFLAG_HAS_BOUNDS   = 0x00000010;
        static final int PFLAG_DRAWN        = 0x00000020;
        static final int PFLAG_FORCE_LAYOUT = 0x00001000;
        /*
         * 在android sdk framework源码里面有许多的位运算,其中赋值一般使用|或运算符,
         * 判断一般使用&与运算符,清空一般使用&和~位运算符.
         */
        void bit() {
    //        int r = PFLAG_DRAWN | PFLAG_HAS_BOUNDS;
    //        System.out.println(r);
    //        System.out.println(Integer.toHexString(r));
    //
    //        r |= PFLAG_FORCE_LAYOUT;
    //
    //        if ((r & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) {
    //            System.out.println("yes");
    //        } else {
    //            System.out.println("no");
    //        }
    
            int privateFlags = 0;
            //为某位赋值
            privateFlags |= (PFLAG_DRAWN | PFLAG_HAS_BOUNDS);
    
            System.out.println(privateFlags);
    
            //清空某位的值
            privateFlags &= ~(PFLAG_DRAWN | PFLAG_HAS_BOUNDS);
            System.out.println(privateFlags);
    
            //为某位赋值
            privateFlags |= PFLAG_FORCE_LAYOUT;
            System.out.println(privateFlags);
    
            //清空某位的值
            privateFlags &= ~PFLAG_FORCE_LAYOUT;
            System.out.println(privateFlags);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
  • 相关阅读:
    总结html5中常见的选择器
    手把手带你使用JWT实现单点登录
    「架构师合集」
    MyBatis使用注解操作及XML操作
    计算机毕业设计之java+springboot基于vue的篮球竞赛预约平台
    QQmlApplicationEngine
    4.9 GHz异帧隔离间距研究
    小程序容器技术如何保障车联网数据安全?
    静态ip和动态ip区别在哪?
    智能合约漏洞案例,DEI 漏洞复现
  • 原文地址:https://blog.csdn.net/ximen250/article/details/126120577