activity的setContentView和activity调用onResume之后的view加载过程及子线程更新UI

 问题1:一直搞不清楚从activity的setContentView到加载我们自己定义的xml布局,这期间加载了几个view?嵌套了多少层级?每一层都是是谁?

思考:一直听说顶层view是一个DecorView,那第二层是哪个?

首先解决的问题是DecorView是怎么被创建的。

先看看Activity的setContentView()调用的是getWindow().setContentView(layoutResID);

public void setContentView(@LayoutRes int layoutResID) {

getWindow().setContentView(layoutResID);

initWindowDecorActionBar();

}

getWindow()返回的是一个 Window对象,Window是一个抽象类,他的子类只有一个就是PhoneWindow。

从而可知getWindow().setContentView(layoutResID);调用的是PhoneWindow的setContentView(layoutResID)。

public void setContentView(int layoutResID) {

if (mContentParent ==null) {

      installDecor();//这里进行了DecorView的创建

}

//下面是把我们自定义的xml布局加载到mContentParent中。mContentParent是在installDecor()中创建

mLayoutInflater.inflate(layoutResID,mContentParent);

}

接下来我们看看installDecor()方法做了哪些事。下面是裁剪的部分代码。

private void installDecor() {

if (mDecor ==null) {

mDecor = generateDecor(-1);//这里创建的DecorView

|

if (mContentParent ==null) {

mContentParent = generateLayout(mDecor);//这里创建的mContentParent.

}

继续看generateDecor(-1)这个方法做了什么?

protected DecorView generateDecor(int featureId) {

    return new DecorView(context, featureId,this, getAttributes());//直接创建一个DecorView并返回。

}

得到mDecor之后,看看mContentParent怎么创建的。看 generateLayout(mDecor)方法。

protected ViewGroup generateLayout(DecorView decor) {

//这里省略了一车对于不同的flags和Feature的判断,做这些判断的目的是要确定接下来要加载的是哪种系统预制的布局。

int layoutResource;//这个layoutResource是系统已经预制有一些布局了,比如有一些有actionBar的有一些没有的。

//得到layoutResource之后会调用下面这个方法去把布局加载出来,并addView进mDecor中。

mDecor.onResourcesLoaded(mLayoutInflater,layoutResource);

//layoutResource加载出来的view中查找contentParent  (ID_ANDROID_CONTENT)ID_ANDROID_CONTENT =com.android.internal.R.id.content;

//这个R.id.content对应的是一个id为content的FrameLayout。由此可得contentParent 是一个FrameLayout

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

return contentParent;

}

onResourcesLoaded(mLayoutInflater,layoutResource);的实现:

void onResourcesLoaded(LayoutInflater inflater,int layoutResource) {

mDecorCaptionView = createDecorCaptionView(inflater);

final View root = inflater.inflate(layoutResource,null);//第二层View的加载

if (mDecorCaptionView !=null) {

if (mDecorCaptionView.getParent() ==null) {

addView(mDecorCaptionView,

new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));

}

mDecorCaptionView.addView(root,

new ViewGroup.MarginLayoutParams(MATCH_PARENT,MATCH_PARENT));

}else {

// Put it below the color views.

        addView(root,0,new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));//把加载出来的xml布局添加到DecorView中。

}

mContentRoot = (ViewGroup)root;

}

从上可知,layoutResource加载出来的布局,作为DecorView的子View。至此,可以得出,第一层View为DecorView,第二层View为(layoutResource加载出来的布局)root。

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//是从root中查找得到的。

所以第三层View为contentParent 即mContentParent =contentParent 。

再由PhoneView.java的setContentView中的 mLayoutInflater.inflate(layoutResID,mContentParent);这句是把我们自定义的xml文件加载的布局添加到mContentParent中。可以知道我们传入的layoutResID对应的xml布局为第四层View。


总结:setContentView第一步先加载DecorView,第二步再加载系统预置的模板布局root(一个LinearLayout),第三步从预置的模板布局中加载mContentParent(一个FrameLayout),第四步把setContentView传入的View或者layoutResID(我们在res中自己定义的xml布局)加载出来的布局添加到mContentParent中。

至此所有的view都加载到了DecorView中了。但是到这还没有显示到屏幕上。


下面是把这些加载到DecorView里面的View绘制到屏幕上的思考。

问题2:,既然view都全部添加到DecorView中了,那怎么说还没绘制到屏幕上呢?我一直以为addView之后就算把View绘制完成了。

view是什么时候开始测量、布局、绘制的? 由于android view的刷新机制16.67ms刷新一次,这个16.67ms依赖于vsync垂直同步信号,当接收到vsync来的时候才开始刷新有变化的view。如果要接收这个vsync信号,就要先注册到发出vsync信号的管理列表中,告诉vsync信号发出的地方说,我这里可以接收vsync信号,vsync 16.67ms时间到之后会通过回调的方式通知接收方。这个注册方式是在ViewRootImpl.java里面scheduleTraversals()方法注册的。vsync发出的地方frameworks\native\services\surfaceflinger\surfaceflinger.cpp中

网上都说view的绘制流程是从activity的onResume()开始的。所以跟踪activity的onResume调用。Activity的onResume()方法是从ActivityThread.java的handleResumeActivity()方法开始的:

public void handleResumeActivity(IBinder token,boolean finalStateRequest,boolean isForward,

String reason) {

View decor =r.window.getDecorView();//通过Window对象获取到顶层的DecorView

ViewManager wm =a.getWindowManager();//获取到WindowManager

WindowManager.LayoutParams l =r.window.getAttributes();//获取到LayoutParams 

l.type =WindowManager.LayoutParams.TYPE_BASE_APPLICATION;//设置type的值,基础的窗口,其他窗口都显示在它之上。


if (!a.mWindowAdded) {

a.mWindowAdded =true;

wm.addView(decor,l);//如果没有添加过就把view添加到windowManager.。

  }

}

解析:

1.ViewManager 是一个接口里面有三个方法 addView,updateViewLayout,removeView,它有一个子类 WindowManager 这个也是一个接口,它继承了ViewManager 。a.getWindowManager()其实就是拿到了WindowManager 的实现类。WindowManagerImpl.java

2.wm.addView(decor,l)实际调用是在WindowManagerImpl.java中。

3.WindowManagerImpl.addview调用的是 mGlobal.addView  ,WindowManagerGlobal →mGlobal。

4.mGlobal.addView → root.setView(view,wparams, panelParentView, userId);       root是ViewRootImpl.java

5.setView()里面有一个重要的方法就是 requestLayout();  然后requestLayout()→scheduleTraversals();至此view就做好了更新的准备了,等到vsync信号来了就可以进行更新了。

顺序:ActivityThread.handleResumeActivity()  →  ViewManager.addView()  →  WindowManagerImpl.addView()→WindowManagerGlobal .addView() → ViewRootImpl.setView() → ViewRootImpl.requestLayout()→ ViewRootImpl.scheduleTraversals()。

Vsync信号来了之后的时序:ViewRootImpl.doTraversal() → ViewRootImpl.performTraversals() → performMeasure() → performLayout() →performDraw() → draw()→ drawSoftware() → mView.draw(Canvas canvas) 结束。

思考:draw方法的入参canvas是哪里来的?ViewRootImpl. drawSoftware()中有一句 canvas =mSurface.lockCanvas(dirty);就是从这里来的,所以平时我们自定义View使用的canvas 就是这个mSurface提供的,我们可以在上面画自己想画的东西。至此View就显示到屏幕上了。


问题3,子线程更新UI

这种更新view的ui需要依赖vsync信号,有16.67毫秒的限制,而且是在主线程更新的View的UI。如果想在子线程做UI的绘制,可以使用SurfaceView或者TextrueView,他们两个提供了在子线程绘制UI的能力,因为他们可以通过SurfaceHolder拿到Surface 进而可以使用surface.lockCanvas()取到Canvas画布,我们只要在Canvas画布上画图就可以直接更新了,不用等vsync信号。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容