onmeasure被执行两次原理解析-kb88凯时官网登录

来自:网络
时间:2023-07-31
阅读:
目录

什么情况下会onmeasure会执行?

进入viewmeasure方法:

void measure(){
    boolean forcelayout = (mprivateflags & pflag_force_layout) == pflag_force_layout;
    boolean specchanged = widthmeasurespec != moldwidthmeasurespec
        || heightmeasurespec != moldheightmeasurespec;
    boolean issepcexactly = measurespec.getmode(widthmeasurespec) == measurespec.exactly
        && measurespec.getmode(heightmeasurespec) == measurespec.exactly;
    boolean matchesspecsize = getmeasuredwidth() == measurespec.getsize(widthmeasurespec)
        && getmeasuredheight() == measurespec.getsize(heightmeasurespec);
    final boolean needslayout = specchanged
        && (salwaysremeasureexactly || !isspecexactly || !matchesspecsize);
    if(forcelayout || needlayout){
        int cacheindex = forcelayout ? -1 : mmeasurecache.indexofkey(key);
        if (cacheindex < 0 || signoremeasurecache) {
            onmeasure(widthmeasurespec, heightmeasurespec);
            mprivateflags3 &= ~pflag3_measure_needed_before_layout;
        } else {
            long value = mmeasurecache.valueat(cacheindex);
            setmeasureddimensionraw((int) (value >> 32), (int) value);
            mprivateflags3 |= pflag3_measure_needed_before_layout;
        }
   }
}

什么时候forcelayout=true:

  • 调用requestlayout
  • 调用forcerequestlayout

什么时候needslayout=true:

  • 当长宽发生改变

什么时候调用了onmeasure>方法:

  • forcelayouy=true
  • 或者mmeasurecache没有当前的缓存

总结:

当调用了requestlayout一定会测发重测过程.当forcelayout=false的时候会去判断mmeasurecache值.现在研究下这个mmeasurecache

class view{
    longsparselongarray mmeasurecache;
    void measure(widthspec,heightspec){
        ---
        long key = (long) widthmeasurespec << 32 | (long) heightmeasurespec & 0xffffffffl;
        int cacheindex = forcelayout ? -1 : mmeasurecache.indexofkey(key);
        if(cacheindex<0){
            onmeasure(widthspec,heightspec);
        }
        moldwidthmeasurespec = widthmeasurespec;
        moldheightmeasurespec = heightmeasurespec;
        mmeasurecache.put(key,widhspec|heightspec);
        ---
    }
}

这里可以看到oldwidthmeasurespecmmeasurecache都是缓存上一次的值,那他们有什么不同呢?不同点就是,oldwidthmeasurespec>不仅仅缓存了测量的spec模式而且缓存了size.但是mmeasurecache只缓存了size.从这行代码可以看出:

long key = (long) widthmeasurespec << 32 | (long) heightmeasurespec & 0xffffffffl;

这里一同运算就为了排除掉spec造成的影响.

//不信你可以试下下面的代码
public class test {
    public static void main(string[] args) {
        long widthmeasurespec = makemeasurespec(10,0);
        long heightmeasurespec =  makemeasurespec(20,0);
        long ss = widthmeasurespec << 32 | (long) heightmeasurespec & 0xffffffffl;
        system.out.println("==========" ss);
    }
    private static final int mode_mask = 0x3 << 30;
    public static int makemeasurespec(int size,
                                      int mode) {
        return (size & ~mode_mask) | (mode & mode_mask);
    }
}
//42949672980
//42949672980
//42949672980

什么时候mprivateflags会被赋值pflag_force_layout.

view viewgrouup的构造函数里面会主动赋值一次,然后在viewgroup.addview时候会给当前viewmprovateflags赋值pflag_force_layout.

为什么onmeasure会被执行两次?

void measure(int widthmeasurespec,int heightmeasurespec){
    ----
    boolean forcelayout = (mprivateflags & pflag_force_layout) == pflag_force_layout; 
    if(forcelayout | needslayout){
        onmeasure()
    }
    ----
}
public void layout(int l, int t, int r, int b){
    ---
    mprivateflags &= ~pflag_force_layout;
    ---
}

在第一次触发到measure方法时,forcelayoyt=true needslayout=true,但是layout方法还没触发到.
在第二次触发到measure>方法时,forcelayout=true needslayout=false,所以还是会进入onmeasure方法.这次会执行layout方法.然后我们在下次的时候forcelayout就等于false了.上面的这一段分析是分析的measure内部如何防止多次调用onmeasure.

分析外部是如何多次调用measure方法的

activity执行到onresume生命周期的时候,会执行windowmanager.addview操作,windowmanager的具体实现类是windowmanagerimpl然后addview操作交给了代理类windowmanagerglobal,然后在windowmanagerglobaladdview里面执行了viewrootimpl.setview操作(viewrootimpl对象也是在这个时候创建的),在viewrootimpl会主动调用一次requestlayout,也就开启了第一次的视图 测量 布局 绘制.

setview的时候主动调用了一次viewrootimpl.requestlayout,注意这个requestlayoutviewrootimpl的内部方法,和view viewgroup那些requestlayout不一样.在viewrootimpl.requestlayout内部调用了performtraversals方法:

class viewrootimpl{
    void performtraversals(){
        if(layoutresuested){
        //标记1
            windowsizemaychanged |= measurehierarchy(host,lp,res,desiredwindowwidth,desiredwindowheight);
        }
        //标记2
        performmeasure()
        performlayout()
    }
    void measurehierarchy(){
        performmeasure()
    }
}

viewrootimpl的执行逻辑你可以看出,在执行performlayout之前,他自己就已经调用了两次performmeasure方法.所以你现在就知道为啥了.

以上就是onmeasure被执行两次原理解析的详细内容,更多关于onmeasure被执行两次的资料请关注其它相关文章!

返回顶部
顶部
网站地图