View的measure()方法是final的,只能被调用,不能被重写.但是提供了一个onMeasure(int widthMeasureSpec, int heightMeasureSpec)的方法给开发者使用.

下面我们就针对android5.0的源码进行分析measure()方法。

首先,我们应该注意到有这样一行代码:

int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);

mMeasureCache用于缓存测量的结果,而这个变量在以前是没有的,说明每一次是真的去重新测量,而现在有了mMeasureCache,系统就不一定会重新测量了。这一点可以看下面的源码去验证:

            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                long value = mMeasureCache.valueAt(cacheIndex);
                // Casting a long to int drops the high 32 bits, no mask needed
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            }

题外话:优化只能从这两点出发:时间换空间(懒加载),空间换时间(做缓存)。

我们再来看看没有缓存的情况下,onMeasure()做了什么。我们发现无论测量有没有缓存,系统都调用了setMeasuredDimensionRaw()方法,这个方法里很简单,对全局变量进行赋值,一个mMeasuredWidth,一个mMeasuredHeight,这两个值就是测量之后的宽和高。这两个值分别对应我们平时调用的view.getMeasuredWidth()和view.getMeasuredHeight()。这两个值就决定了这个view有多大,这个view的大小不能被我们直接设置,没有setMeasuredWidth和setMeasuredHeight之类的方法,宽和高只能通过系统自己内部的逻辑进行测量得出。

无论是onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法还是measure(int widthMeasureSpec, int heightMeasureSpec)都有两个参数,这两个参数不仅代表着父view给当前view分配的宽和高,还代表着子view的测量模式。测量模式有三种:MeasureSpec.AT_MOST, MeasureSpec.EXACTLY, MeasureSpec.UNSPECIFIED;接下来我们就讲一讲这三个模式代表什么意思。

MeasureSpec.AT_MOST

代表最多:父view能给当前子view空间的最大值,这个值与子view自己测量的没有关系。

取父view给定值和子view自身测量值中相对小的。

MeasureSpec.EXACTLY

代表精确:父view规定子view所测量的值,这种模式下,子view不用在测量,直接使用父view传递过来的值就可以。

MeasureSpec.UNSPECIFIED

代表未知

这三个模式的值就隐含在了int widthMeasureSpec, int heightMeasureSpec这两个参数中。widthMeasureSpec是int类型的值,有32位,其中最高的2位就代表着他的测量模式。接下来的30位代表父view传递进来的大小。

int measureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);

内存和GC

为什么系统要把两个数据合成到一起传递过来呢?如果把两个int类型的数据直接拆分成4个int类型进行传递,内存就会由2个32位变成4个32位。试问节约这么点内存数据有必要吗?有必要。如果布局层级足够多,一次绘制onMeasure()方法的调用会有成百上千次。会额外开辟不少的内存。这是其一,其二,java虚拟机内存是只增加不减少。打个比方,应用占用了20M的内存,现在内存不足了,你向系统申请更多的内存,系统给了你30M,然后你又占了30M,又内存不足了,系统给你40M,等你占到100M了,突然你把资源全部回收了,100M系统还是会继续保留给你,系统不会回收回去了,只是空着留着我们自己使用。所以我们尽可能少占用系统内存,要不然搞一次内存就增加不少了,(疑问:系统会识别占用内存大的应用进行回收,内存大是指使用内存大还是分配内存大?)还有系统给应用分配内存之前,一定会进行GC,垃圾回收的时机对程序员来说是不确定的,对于虚拟机来说是确定的,它只在特定的时候回收,不是说我刚刚创建了一个对象,我把这个对象又置为null,那么他就会被回收,它不会被回收,当我们的应用内存不足的情况下,打个比方,系统给我们分配了64M的内存,我们用了60M了,接着我们再想创建对象,创建对象时发现内存不足,请求系统分配内存,系统在给我们分配内存之前会进行一次GC,把不要的内存回收掉,这时候我们置为null的对象才会被回收。内存回收的时候,有一个非常显著的特点:清理内存的时候整个应用的进程将会被挂起。也就是说整个应用的代码将不会被执行,这也就是我们看到的卡了,这个时候你的代码一句都不会被执行,因为内存遍历的时候它有很多种算法,我只和大家讲一种通用的算法。以下面代码为例:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height;

        View view = new View(this.getContext());

        if (orientation == 0){
            width = drawable1.getIntrinsicWidth() + drawable2.getIntrinsicWidth();
            height = Math.max(drawable1.getIntrinsicHeight(), drawable2.getIntrinsicHeight());
        } else {
            width = Math.max(drawable1.getIntrinsicWidth(), drawable2.getIntrinsicWidth());
            height = drawable1.getIntrinsicHeight() + drawable2.getIntrinsicHeight();
        }

        setMeasuredDimension(width, height);

    }

它会从应用的栈(方法都是在栈中执行的)、静态区、常量池里面的引用开始找起。onMeasure()方法进了栈,会创建一个引用。方法内部的变量(包括传进来的形式参数和new出来的对象)在方法执行完毕之后我们就没有办法再找到它了。是不是被回收了呢?不一定,对象就在堆里面它不一定被回收了。但是我们没有办法找到他,为什么找不到?因为没有引用指向它了。对象的生命周期只在onMeasure()方法中mPrivateFlags,方法一旦执行结束,这个对象的引用就被清掉了。但是系统可以找到哪些对象有引用。系统从所有的方法栈中找到所有的变量。通过变量去找看它有没有指向一个对象。若有对象指向,会给对象打上标记,遍历完成后,没有打标记的就是没有引用的。系统就会把这些没有引用的干掉。所以这个时候,系统要避免一些误操作(系统刚刚遍历的时候,某个对象还是没有引用的,如果这个时候代码还能执行,我们又搞了个引用过去,清掉就会很危险)。所以系统在遍历的时候,不允许有任何代码执行操作。所以说这个时候你的应用就卡住了。这就是内存回收机制。

所以说系统在这里要避免大量的创建对象,对象一旦创建多了,就会去申请内存,一申请内存就卡住了。一卡住了就是反映到界面上就是卡顿。

mPrivateFlags

见Measure(二)

results matching ""

    No results matching ""