Activity的创建启动测量


仅用于自学总结,不便于他人学习;如有阅读,如有意见,望提探讨;

Activity以及Application的创建 以及生命周期的调用;

程序入口ActivityThread.main 启动,Activity的attach()方法中,IActivityManager通过AMS代理,在AMS进程中拿到pid和uid,组装了Application的信息,通过Binder和ActivityThread通信,调用它的binfApplication方法,ActivityThread的该方法中通过Handler发消息,然后接收消息反射创建Application,创建成功后调用了它的onCreate方法;
AMS进程中,bindApplication之后就通过attachApplicationLocked(app)等一系列调用,调用到了ActivityThread#ApplicationThread#scheduleTransaction()方法,同样经过一系列调用也是通过Handler发消息,然后接收消息调用handleLaunchActivity,该方法中调用performLaunchActivity反射创建Activity,然后调用createActivityOnCreate->performCreate->onCreate();

setContentView

上面Activity的onCreate调用之后就是调用setContentView()方法

Activity.setContentView->getWindow().setContentView->PhoneWindow.setContentView

PhoneWindow是在Activity的attach中初始化的;

在PhoneWindow的setContentView方法中,对mContentParent进行了创建赋值,通过调用installDecor方法;最后通过布局加载器将我们传进来的main.xml解析到mContentParent中,mContentParent对应着布局中的content布局;

在installDecor方法中,先初始化创建了DecorView,然后对一些window属性进行赋值处理;然后加载了mContentRoot布局,它内部有两个子View,一个是ActionBar,一个是content,其中content对应着mContentParent;将mContentRoot添加到DecorView中,installSecor最后将mContentParent返回,然后同上所诉:最后通过布局加载器将我们传进来的main.xml解析到mContentParent中

到目前为止,通过setContentView方法,创建了DecorView和加载了我们提供的布局,但是这时,我们的View还是不可见的,因为我们仅仅是加载了布局,并没有对View进行任何的测量、布局、绘制工作。

将DecorView添加至Window

每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。

还是说到上面的Activity的创建过程中,在最后启动Activity的部分,当在Activity#onCreate中完成上面的setContentView()之后,ActivityThread#handleLaunchActivity还会调用ActivityThread#handResumeActivity方法;
在该方法会获取Window对象和DecorView对象;还会获取WindowManager对象;
最后调用wm的addView(decor,l)方法;

WindowManager的实现类是WindowManagerImpl,在他的addView(view)方法中,创建了ViewRootImpl对象,并调用了ViewRootImpl#setView(view),在该方法中,通过WMS,从而将DecorView添加到了Window中;

ViewRootImpl、DecorView、WMS三者会彼此关联;

最后通过WMS调用ViewRootImpl#performTraerals方法来开始View的测量、布局、绘制流程。

ViewRootImpl#performTraerals

该方法中会调用3个方法

  • performMeasure(childWidthMeasureSpe,childHeightMeasureSpec)
  • performLayout(lp,desiredWindowWidth,desiredWindowHeight)
  • performDraw()

Measure测量

MeasureSpec

MeasureSpec是一种测量存储规格:他是一个32位的int值,它的高两位用来存储mode,低30位用来存储size;

performMeasure()

performMeasure(childWidthMeasureSpe,childHeightMeasureSpec)中的childWidthMeasureSpe,childHeightMeasureSpec都是通过getRootMeasureSpec方法获取到的;因为它是DecorView,是第一个父View,所以它的测量规则是来自于屏幕宽高和自身的layoutParams;

该方法中通过判断它的测量模式来确定,如果是确定尺寸则为确定尺寸,如果未MATCH_PARENT则为屏幕宽度,WRAP_CONTENT则为0到屏幕宽度之间;

getRootMeasureSpec方法确定了mDecor的测量规则;

然后调用了performMeasure()方法;内部mDecor.mesure(childWidthMeasureSpe,childHeightMeasureSpec),此时从顶级View开始了测量流程;

DecorView继承于FrameLayout,DecorView.measure->FrameLayout.measure->View.measure->View.onMeasure->DecoreView.onMeasure->FrameLayout.onMeasure;

在该方法中,遍历子View,去掉GONE的View,然后对每一个子View进行测量:measureChildWidthMargins;

测量之后保存测量结果:

setMeasureDimension(
        resolveSizeAndState(maxWidth,widthMeasureSpec,childState),
        resolveSizeAndState(maxHeight,heightMeasureSpec,childState)
)

View的测量

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 1
}

内部调用了getChildMeasureSpec方法;该方法中确立了子View如何获取测量规则:

子View的LayoutParams\父容器SpecMode EXACTLY AT_MOST UNSPECIFIED
精确值(dp) EXACTLY childSize EXACTLY childSize EXACTLY childSize
match_parent EXACTLY parentSize AT_MOST parentSize UNSPECIFIED 0
wrap_content AT_MOST parentSize AT_MOST parentSize UNSPECIFIED 0

parentSize的意思是父View剩余的空间Size;

  • 当子View为EXACTLY时,无论父类是什么模式,都是取得childSize;
  • 当子View为AT_MOST时,父类无论是EXACTLY还是AT_MOST都是parentSize;因为如果父View是EXACTLY时,父View是将自己的剩余空间都给了子View;如果是AT_MOST,同理,因为不知道子View最大会用到多少,所以把最大值给过去,子View根据自己的实际情况使用,但是不可以超过最大值;
  • 当子View为MATCH时,同AT_MOST理

上面的表格可以反馈出一个问题就是:

从顶层View开始测量,一种有4中形式(其实是3种);

UNSPECIFIED暂不考虑;

如果父View是准确值模式则直接往下传测量准则时,size就是准确值;
如果父View是match_parent时,基本上就是当前的所有size[pading+margin要去掉]
如果父View是wrap_content时,则它的长度可能是[0~maxSize]之间的值,此时因为不确定size,所以需要子类测量自己后来告诉父类size最大是多少,所以此时父类做的就是直接把maxSize下发给子类,然后子类再来根据自己的实际来测量;

这里其实一直有一个问题:
那就是当父View为准确值时,这个没问题,但是当它为AT_MOST时,也就是父View的宽/高不确定[之后就只说宽,高同理],下发测量规则时,总是把剩余宽度都下发过去,如果有的子View也是ViewGroup,且宽度也是AT_MOST,那就继续将该宽度[剩余宽度]下发,一直到最后的子View中的onMeasure方法中

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

可以看到,在getDefaultSize中,AT_MOST和EXACTLY是一样的,取得是测量规则中的size,也就是说,如果该View的所有父View都没有具体的size,那他的宽度就是DecorView的宽度,也就是屏幕宽度,所以一般自定View,如果需要自定义的大小尺寸,都需要重写onMeasure方法,区分一下match_parent和wrap_content的尺寸区别;

还有一个说法,很多人都说,针对这种父View是AT_MOST的情况,都是通过测量子View的尺寸从而再次测量自己的尺寸;但是就我个人而言,我没看到再次测量自己的方法调用,于是我很疑惑,难道他不用再次测量自己吗,肯定需要的,不然怎么知道它自己的测量大小;setMeasuredDimension方法只是保存了剩余的最大可能值,却不是精确值,那他最后的精确值是怎么测量的呢?

我后来琢磨了一下,他不需要在测量这知道,它只需要将它所有的子View都测量一遍,然后在onLayout的时候,按照测量值进行排布的时候就会知道最后的精确值,在onMeasure之后知道这个精确值也没啥用?仅仅是猜测;

又看了下,发现以上猜测不对:有以上猜测纯粹是因为看到FrameLayout#onMeasure中首先编辑测量子View,然后保存测量结果;但是它后面又对FrameLayout为AT_MOST子View为MATCH_PARENT的情况进行了出了,对该种情况下的子View进行了二次测量;然后就没了;

我是看到了上方这样才有此猜想;一时不解,然后去看到了LinearLayout#onMeasure,以为也会出现此种情形,结果发现LinearLayout将setMeasuredDimension放到了最后,也就是它确实是保存了准确的测量结果;那FrameLayout#onMeasure又是怎么回事,细想一下就明白了,其实它在对子View进行测量之后,取得最大值之后进行保存,他的宽/高就已经定了,不会发生改变,只剩剩下那种情况的二次测量,也只是在这种定了的情况下对那些子View为MATCH_PARENT的进行具体值赋值,因为是基于那个最大值进行操作,所以怎么也不会超过最大值,那就是最终保存的测量结果不会造成任何影响。

参考资料:
Android View源码解读:浅谈DecorView与ViewRootImpl
Android View 测量流程(Measure)完全解析
Android View 布局流程(Layout)完全解析
Android View 绘制流程(Draw) 完全解析
Activity的创建,和生命周期的调用
Activity XML 布局文件的加载
《Android开发艺术探索》第4章View的工作原理 -任玉刚


你可能感兴趣的:(Activity的创建启动测量)