初探Android中Window与DecorView

Android中View可以说是最为重要的几个地方之一,包括事件分发,测量,绘制等等,都是非常常见的情况。那么我们要想好好掌握这些知识,就得深入了解Andorid整个View从开始到完成所经历的一系列工作。本文分析的源代码均来自Android API 24。

Activity和Window

在Android中,Activity并不负责视图控制,它只是控制生命周期和处理事件,真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口,也就是说Activity如果没有Window,那就和Service没多大差别了。
Window第一次出现在Activity中是在ActivityThread调用Activity的attach()方法时,通过PolicyManager创建window,实现callback方法,所以,当window接收到
外界状态改变时,会调用activity的方法:

final void attach(Context context, ActivityThread aThread,
    Instrumentation instr, IBinder token, int ident,
    Application application, Intent intent, ActivityInfo info,
    CharSequence title, Activity parent, String id,
    NonConfigurationInstances lastNonConfigurationInstances,
    Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
....

mWindow = new PhoneWindow(this, window); //在这里直接new一个Window
mWindow.setCallback(this);//设置回调接口,当window接收系统发送给它的例如键盘和触摸屏事件,就可以通知Activity,Activity做出相应的处理
.....
//设置窗口管理器
mWindow.setWindowManager(
     (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        mToken, mComponent.flattenToString(),
        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
.....
}

在attach()中,Activity新建一个PhoneWindow实例作为成员变量,这是Window的唯一一个子类。然后设置mWindow的WindowManager。

DecorView

首先简单介绍一下DecorView,这里引用任玉刚大神的《Android开发艺术探索》书中的介绍:它是一个顶级View,内部会包含一个竖直方向的LinearLayout,这个LinearLayout有上下两部分,分为titlebar和contentParent两个子元素,contentParent的id是content,而我们自定义的Activity的布局就是contentParent里面的一个子元素。View层的所有事件都要先经过DecorView后才传递给我们的View。那么DecorView是何时出现的呢??又是如何和window产生联系呢??


Activity类的成员变量

首先,如上图我们可以看到Activity中包含了Window,DecorView,WindowManager等成员变量,那么我们就从Activity的创建过程中去逐步寻找它们。
首先在Activity中的onCreate方法中我们都会写一句setContentView的方法调用,将我们自定义的Activity布局传入。那么我们跟进该方法去看一下具体实现。



通过getWindow来获取成员变量mWindow。
image.png

然后调用window的setContentView()方法。该方法来进行我们Activity的布局设置。然后又接着调用了initWindowDecorActionBar()方法来进行对ActionBar的新建和初始化。我们暂时不管该方法。
mWindow类型是Window,是一个接口,而在安卓中实现Window接口的实现类只有PhoneWindow,所以进入PhoneWindow查看setContentView()方法的具体实现。先上源代码:



首先会在上图标记1处判断mContentParent是否为空,如果为空,会执行installDecor()方法,那么mContentParent是什么呢?
通过定义可以看出,mContentParent是一个ViewGroup类型的变量,它是Activity的一个成员变量。我们所写的setContetView()所设置的布局文件就加到这个视图中。根据官方解释它可能是mDecor自身,也可能是mDecor的子View。
继续回到setContentView中看代码,如果mContentParent为空,那么会执行installDecor()方法,跟进这个方法,顾名思义,新建DecorView。下面给出源代码:

在installDecor方法中标注1处,如果mDecor为空(mDecor是Window持有的一个成员变量,指的就是DecorView),那么在 generateDecor()中去实例化新建DecorView,我们继续跟进generateDecor()查看源代码:

在这里前面一段代码都是初始化所需要的context,最后返回一个实例化的DecorView()。在DecorView构造方法中进行初始化工作。


image.png

上图是DecorView的一些初始化工作,就不再展开,有兴趣的同志们可以研究。
返回到installDecor()方法中继续往下,到标记处2,会判断mContentParent是否为空,如果为空,通过 generateLayout()来将我们具体的布局加载到DecorView中。跟入 generateLayout()方法查看源代码(由于方法中代码过长不便于截图,我贴出关键代码):

...//代码前面都是在获取主题相关 

//在这个方法里将mContentParent的布局转换并添加到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//具体代码在下一个代码框中

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// contentParent就是root的父布局。
return contentParent;

final View root = inflater.inflate(layoutResource, null);//将mContentParent布局转换为View
mDecor.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));//将mContentParent加入到DecorView中
mContentRoot = (ViewGroup) root;//mContentRoot就是mContentParent视图

至此generateLayout()方法分析完毕,返回contentParent变量,赋值给mContentParent成员变量。

随后上述步骤也是installDecor()方法的主要代码,因此继续返回到setContentView方法继续查看(把setContentView的图继续贴下来避免返回去翻图):

图中标注处2的代码,则是将我们的自定义布局加入到mContentParent(也就是id为R.id.content)布局中。

最后标记处3则是通过回调来通知ActivityContent已经发生了变化,由Activity来做出相应的处理。


DecorView和Window

至此已经setContentView已经完成,DecorView也已经新建,我们自定义布局也加入到mContentParent布局中,但是此时mDecorView还没有被WindowManager正式添加到Window中。因此Window此时还无法接受外界的信息,也无法提供具体的功能。
所以在activity中的onResume()方法中调用了makeVisible()方法来进行添加,同时也是把Window加入到WindowManager中

void makeVisible() {  
  if (!mWindowAdded) {     
   ViewManager wm = getWindowManager(); //获得WindowManager(WindowManager extends ViewManager)
   wm.addView(mDecor, getWindow().getAttributes());   //将DecorView加入到Window中
   mWindowAdded = true;   
  }   
  mDecor.setVisibility(View.VISIBLE);
}

至此整个decorView的加载过程和Activity的Window创建过程已经完成,Decor将会显示出来呈现在手机屏幕上.


ps:由于是大二萌新第一次写东西,所以很多地方还很模糊,希望前辈们多多指点错误,也希望能继续加油记录自己的点点滴滴
再ps:本文参考和学习了任玉刚大神的《Android开发艺术探索》和陈育大神的文章(http://www.jianshu.com/p/687010ccad66)

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

推荐阅读更多精彩内容