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 pendingResults, List 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 pendingResults, List 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中


    
    

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

你可能感兴趣的:(Window/Activity/View之间的关系)