Activity、Window、View关系 —— 浅谈

1、相关的类

Activity:  Activity、ActivityThread、ActivityManager、ActivityManagerService

Window:  Window、PhoneWindow、WindowManager 、WindowManagerImpl、WindowManagerGlobal

View:View、ViewGroup、ViewManager、DecorView、ViewRoot、ViewRootImpl

Activity/window/View的联系:

1、Activity中 getWindow(), 建立window 和Activity之间的联系

2、DecorView是窗口最顶层的视图,通过wm.addView(mDecor, l) ,建立Window和DecorView之间的联系

3、根据指定的应用theme/feature特性找到不同的窗口修饰布局文件,建立mDecor和mContentParent之间的联系

4、通过findViewById(R.id.content)得到mContentParent,添加自定义xml文件到mContentParent中,得到mContentParent和自定义xml文件间的联系

Activity、Window、View关系 —— 浅谈

Activity、Window、View关系 —— 浅谈

Activity、Window、View关系 —— 浅谈

public interface ViewManager {
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

windowManagerImpl#addView()      

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

Activity 依赖于 Window, Window 依赖于 WindowManager (WindowManagerGlobal最终处理) 来实现对 View 的各种操作(addView/ updateViewLayout/ removeView)

从startActivity开始,它会调用到Instrumentation,然后Instrumentation通过Binder向AMS(ActivityManagerService)发请求,通过PIC启动Activity。

启动Activity所执行的操作: 
1、从ActivityClientRecord中获取待启动的Activity组件信息 
2、通过Instrumentation的newActivity方法使用类加载器创建Activity对象 
3、通过LoadedApk的mackApplication方法来尝试创建Application对象(如果Application已经创建,则不会重复创建) 
4、创建ContextImpl对象,并通过Activity的attach方法来完成一些重要数据的初始化(包括让Activity跟Window关联) 
5、调用Activity的onCreate方法

ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.

    Activity a = performLaunchActivity(r, customIntent);
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    
            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, r.configCallback);

            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
   
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
            
        }

    return activity;
}

final void handleResumeActivity(IBinder token,
        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    
    r = performResumeActivity(token, clearHide, reason);

       
            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;
           
            ViewRootImpl impl = decor.getViewRootImpl();
            wm.addView(decor, l);
               
            ViewManager wm = a.getWindowManager();
            View decor = r.window.getDecorView();
            wm.updateViewLayout(decor, l);
}

Activity.java

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,
        Window window, ActivityConfigCallback activityConfigCallback) {

    mWindow = new PhoneWindow(this, window, activityConfigCallback);
 
}

WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
   
        ViewRootImpl root;
        View panelParentView = null;
        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        try {
            root.setView(view, wparams, panelParentView);
        } 
    }
}

Activity.setContentView    ->    Window.setContentView    ->    PhoneWindow.setContentView

Activity    ->    Window    ->    DecorView (View的操作由WindowManager来实现)

如果Activity 的 setContentView 中参数是 layoutResId, 在PhoneWindow 中则通过 LayoutInflator.inflate(layoutResId, mContentParent) ; 如果Activity 的 setContentView 中参数是 view, 在PhoneWindow 中则通过 mContentParent.addView(view, params) ,所以说界面绘制全是由Window的实现类PhoneWindow来完成的。

此时只是设置DecorView加载了布局资源文件,还没有对View进行测量、布局、绘制工作,需要经过一系列过程触发 ViewRootImpl.performTranversalsfa

View是Android中的视图呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window (activity、Dialog、Toast)

Activity.java

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

Window.java

public abstract void setContentView(@LayoutRes int layoutResID);

PhoneWindow.java

@Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

PhoneWindow.java

@Override
public void setContentView(View view) {
    setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
        mContentParent.addView(view, params);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

PhoneWindow.java

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        }
}

PhoneWindow.java

protected DecorView generateDecor(int featureId) {
    return new DecorView(context, featureId, this, getAttributes());
}

PhoneWindow.java

protected ViewGroup generateLayout(DecorView decor) {
    layoutResource = R.layout.screen_simple;
        
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    
    return contentParent;
}

DecorView是整个ViewTree最顶层的View,里面包括了一个 title 和 frameLayout,mContentParent是DecorView本身或者DecorView的一个子元素,看需不需要title 

R.layout.screen_simple   

<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>