Android视图架构详解

作者: ztelur
原文地址:
http://blog.csdn.net/u012422440/article/details/51173387

最近一直在研究View的绘制相关的机制,发现需要补充一下Android View Architecture的相关知识,所以就特地研究了一下这方面的代码,写成本篇文章。

为了节约你的时间,本篇文章内容大致如下:

  • ActivityDecorViewPhoneWindowViewRoot的作用和相关关系。

Android View Architecture

先来几张图,大致展现一下Android 视图架构的大概:

Android View Architecture
View各类关系图
View树状图

Activity和Window

众所周知,Activity并不负责视图控制,它只是控制生命周期和处理事件,真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口,也就是说Activity可以没有Window,那就相当于是Service了。在ActivityThread中也有控制Service的相关函数或许正好印证了这一点。

ActivityWindow的第一次邂逅是在ActivityThread调用Activityattach()函数时。

//[window]:通过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 = PolicyManager.makeNewWindow(this);
    //当window接收系统发送给它的IO输入事件时,例如键盘和触摸屏事件,就可以转发给相应的Activity
    mWindow.setCallback(this);
    .....
    //设置本地窗口管理器
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    .....
}

attach()中,新建一个Window实例作为自己的成员变量,它的类型为PhoneWindow,这是抽象类Window的一个子类。然后设置mWindowWindowManager

Window,Activity和DecorView

DecorViewFrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下两个部分(具体情况和Android版本及主体有关),上面的是标题栏,下面的是内容栏。在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,而内容栏的id是content,在代码中可以通过ViewGroup content = (ViewGroup)findViewById(R.android.id.content)来得到content对应的layout

Window中有几个视图相关的比较重要的成员变量如下所示:

  • mDecor:DecorView的实例,标示Window内部的顶级视图;
  • mContentParent:setContetView所设置的布局文件就加到这个视图中;
  • mContentRoot:是DecorView的唯一子视图,内部包含mContentParent,标题栏和状态栏。

Activity中不仅持有一个Window实例,还有一个类型为ViewmDecor实例。这个实例和Window中的mDecor实例有什么关系呢?它又是什么时候被创建的呢?

二者其实指向同一个对象,这个对象是在Activity调用setContentView时创建的。我们都知道ActivitysetContentView实际上是调用了WindowsetContentView方法。

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) { //[window]如何没有DecorView,那么就新建一个
        installDecor(); //[window]
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    ....
    //[window]第二步,将layout添加到mContentParent
    mLayoutInflater.inflate(layoutResID, mContentParent);
    .....
}

代码很清楚的显示了布局文件的视图是添加到mContentParent中,而且Window通过installDecor来新建DecorView

//[window]创建一个decorView
private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(); //直接new出一个DecorView返回
        ....
    }
    if (mContentParent == null) {
        //[window] 这一步也是很重要的.
        mContentParent = generateLayout(mDecor); //mContentParent是setContentVIew的关键啊
        .....
    }
    ....
}
protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    .......
    //[window] 根据不同的style生成不同的decorview啊
    View in = mLayoutInflater.inflate(layoutResource, null);
    // 加入到deco中,所以应该是其第一个child
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    mContentRoot = (ViewGroup) in; //给DecorView的第一个child是mContentView
    // 这是获得所谓的content 
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    }
    .....
    return contentParent;
}

从上述的代码中,我们可以清楚的看到mDecormContentParentmContentRoot的关系。

那么,Activity中的mDecor是何时被赋值的?我们如何确定它和Widnow中的mDecor指向同一个对象呢?我们可以查看ActivityThreadhandleResumeActivity函数,它负责处理Activityresume阶段。在这个函数中,Android直接将Window中的DecorView实例赋值给Activity

final Activity a = r.activity;
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;

Window,DecorView 和 ViewRoot

ViewRoot对应ViewRootImpl类,它是连接WindowManagerServiceDecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。ViewRoot可以被理解为“View树的管理者”——它有一个mView成员变量,它指向的对象和上文中WindowActivitymDecor指向的对象是同一个对象

我们来先看一下ViewRoot的创建过程。由于ViewRoot作为WindowMangerServiceDecorView的纽带,只有在WindowManager将持有DecorViewWindow添加进窗口管理器才创建。我们可以查看WindowMangerGlobal中的addView函数。对WindowManager不太熟悉的同学可以参考《Window和WindowManager解析》

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
        // 创建ViewRootImpl,然后将下述对象添加到列表中
    ....
    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    ....
    try {
        // 添加啦!!!!!!!!这是通过ViewRootImpl的setView来完成,这个View就是DecorView实例
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
      ....
    }
    ....
}

那么,Window是什么时候被添加到WindowManager中的呢?我们回到ActivityThreadhandleResumeActivity函数。我们都知道Activityresume阶段就是要显示到屏幕上的阶段,在Activity也就是DecorView将要显示到屏幕时,系统才会调用addView方法。

我们在handleResumeActivity函数中找到了下面一段代码,它调用了ActivitymakeVisible()函数。

// ActivityThread
r.activity.makeVisible();

//Activity
    //[windows] DecorView正式添加并显示
    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

我们通过源代码发现,正式在makeVisible函数中,系统进行了Window的添加。

引用

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

推荐阅读更多精彩内容