Android学习Android知识Android开发

Window/Activity/View之间的关系

2017-06-01  本文已影响340人  juexingzhe

今天我们从最常用的一个方法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

上一篇下一篇

猜你喜欢

热点阅读