Window/Activity/View之间的关系

今天我们从最常用的一个方法setContentView来看看Window/Activity/View之间的关系。先看几个基本概念:

1.Window表示一个窗口的概念,是一个抽象类,具体实现是PhoneWindow,WindowManager是外界访问Window的入口,Window的具体工作是在WindowManagerService,WindowManager和WindowManagerService的交互是IPC过程;
2.Activity是Android提供的组件,用于向用户直接展示一个界面,而界面UI是由View组成的,Activity中的顶级View是DecorView,setContentView所设置的布局就是加载到DecorView中;
3.Android中所有的视图都是通过Window来呈现的,Window是View的直接管理者,DecorView也是需要添加到Window中才能显示。

加下来我们从源码角度来一步步验证Window/Activity/View之间的关系。

1.Window与Activity

我们一般都是调用startActivity(intent)来启动一个具体的Activity,而真正的实现是ActivityManagerService,这个我们就不在本文多说,有需要的可以参考下老罗的博客。准备工作完成后,AMS会通过ApplicationThreadProxy进行IPC:

class ApplicationThreadProxy implements IApplicationThread{
    private final IBinder mRemote;

    public ApplicationThreadProxy(IBinder remote) {
        mRemote = remote;
    }

    public final IBinder asBinder() {
        return mRemote;
    }

    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,ActivityInfo info, Configuration curConfig, Configuration overrideConfig,CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState,List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
      ……     
     mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION,data, null, IBinder.FLAG_ONEWAY);
        data.recycle();
    }
}

之后调用到ApplicationThread的scheduleLaunchActivity方法,ApplicationThread是ActivityThread的内部类。在方法内部通过通过Handler切换到ActivityThread。

private class ApplicationThread extends ApplicationThreadNative {
    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,ActivityInfo info, Configuration curConfig, Configuration overrideConfig,CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState,List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,boolean notResumed, boolean isForward, ProfilerInfo profilerInfo){
                ……
                sendMessage(H.LAUNCH_ACTIVITY, r);
    }
}

在Handler中会调用handleLaunchActivity方法

public void handleMessage(Message msg) {
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

            r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
    ……
}

在handleLaunchActivity方法中会调用performLaunchActivity,该方法的一个重要工作就是组装Activity,然后会调用一个比较重要的方法

activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window);

在activity.attach方法中会创建Window:

mWindow = new PhoneWindow(this, window);
mWindowManager = mWindow.getWindowManager();

绕了这么一大圈我们终于知道在每个Activity中都有一个Window,Activity通过Window来加载显示布局文件中的View,这样看来Activity有点类似装配工人,通过Window来装载View。接下来我们来看看View和Window的关系。

来个分割线休息下~~~

美丽的西安城墙.jpg

2.setContentView看DecorView

我们的布局文件怎么显示到屏幕上的呢?一般我们都是通过setContentView(@LayoutRes int layoutResID)来加载布局文件,那么我们跟踪这个方法看看能不能找到Window和View的关系。可以看到方法里面调用的是window. setContentView,这个window就是我们上面分析得到的PhoneWindow,那么我们再跟踪进去看看。

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

在PhoneWindow的setContentView()方法中先进行installDecor,这个看名字就可以大概猜出window在这里面会安装DecorView,也就是视图的顶级View。

if (mContentParent == null) {
    installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    mContentParent.removeAllViews();
}

installDecor()方法比较长,我们挑重点的看,首先就是生成DecorView,然后再生成mContentParent,mContentParent 是一个ViewGroup,那么DecorView和mContentParent有什么关系?

if (mDecor == null) {
    mDecor = generateDecor(-1);
    ……
} else {
    mDecor.setWindow(this);
}
if (mContentParent == null) {
    mContentParent = generateLayout(mDecor);
    ……
}

接着跟到generateLayout方法中,contentParent就是layout布局文件中R.id.content的FrameLayout

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

DecorView是一个FrameLayout,然后DecorView会加载一个系统布局layout, 常用的layout在后面列出。

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks

R.layout.screen_swipe_dismiss
R.layout.screen_title_icons
R.layout.screen_progress
R.layout.screen_custom_title
R.layout.screen_action_bar
R.layout.screen_title
R.layout.screen_simple_overlay_action_mode
R.layout.screen_simple

我们就挑出最后一个布局R.layout.screen_simple看看,有木有看到有个id是content的FrameLayout,这个就是前面说到的mContentParent,我们平时setContentView加载的Activity layout就是加载到mContentParent中

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

3.Window与View

上面分析了setContentView的来龙去脉,但是到目前为止还没看出Window和DecorView的关系,也就是还不能显示UI。
还记得我们前面分析Activity和Window关系时,在handleLaunchActivity中其实除了performLaunchActivity方法还会调用另外一个重要的方法handleResumeActivity,见名知意应该就是在这个方法中显示View,我们跟踪进去看看。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        ……

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        ……
}        

在handleResumeActivity方法里面会调用Activity的makeVisible()方法,是不是如此清晰,通过windowManager.addView添加DecorView进行显示。

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

4.总结

到这里我们的Window/Activity/View三角恋关系分析就到此结束了,做个小小的总结。从Activity启动的角度分析了Activity和Window的关系,每个Activity都有一个对应的Window,Activity通过setContentView来加载布局到DecorView的contentParent中,然后通过Window.addView进行加载显示,其实关系也很清晰。

谢谢大家!
欢迎关注公众号:JueCode

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

推荐阅读更多精彩内容