Activity、Window、View关系(API27 源码)

版权声明: https://blog.csdn.net/ZHENZHEN9310/article/details/86487937

1、相关的类

Activity: Activity、ActivityThread、ActivityManager、ActivityManagerService

Window: Window、PhoneWindow、WindowManager 、WindowManagerImpl、WindowManagerGlobal

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

1、Activity/window/View的联系?

Activity是一个应用组件,用户可与其提供的屏幕进行交互,但view的add/update/remove不是由Activity直接操作的;而是每个 Activity都会有一个对应的,用于绘制其用户界面的Window,由这个window的windowManager来对view进行管理操作;

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

2、Activity/window/View是怎么建立联系的?

1、Activity#attach()内,创建window对象并绑定windowManager,建立Activity和window/windowManager之间的联系。

2、Activity#onResume()之后,wm.addView(mDecor, l),建立window/windowManager和DecorView之间的联系。

3、mDecor是哪里来的?

4、Activity的attach()/resume()是在什么时候调用的?

前面我们说了Activity的启动过程,Activity的生命周期都是由AMS调度的;…最终会向主线程里的Handler对象发出Message;

  • LAUNCH_ACTIVITY:会依次执行activity的attach()、onCreate()、onStart()
  • RESUME_ACTIVITY:会执行activity的onResume()

下面对各个细节进行解说

1、Activity和Window进行绑定

  • ActivityThread#handlerLaunchActivity():mH收到系统发出的LAUNCH_ACTIVITY这么一条Message
  • ActivityThread#performLaunchActivity():实例化activity,然后依次执行activity的attach()、onCreate()、onStart()、onRestoreInstanceState()
  • Activity#attach():实例化Window并设置windowManager,即Activity与Window进行绑定

ActivityThread#handlerLaunchActivity()

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        Activity a = performLaunchActivity(r, customIntent);
    }

ActivityThread#performLaunchActivity()

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        Activity activity = null;
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity( //通过类加载的方式加载这个activity
                    cl, component.getClassName(), r.intent); 

                Window window = null;
                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); //attach

                if (r.isPersistable()) { //执行activity的onCreate
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
  
                if (!r.activity.mFinished) {
                    activity.performStart(); //执行activity的onStart()
                }
                if (!r.activity.mFinished) {
                    if (r.isPersistable()) {
                        if (r.state != null || r.persistentState != null) { //执行activity的onRestoreInstanceState()
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                    r.persistentState);
                        }
                    } else if (r.state != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                    }
                }
            }
            mActivities.put(r.token, r);
        return activity;
    }

Activity#attach()

 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); //创建Window对象
 
        //省略mWindow的配置参数
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
      
        mWindowManager = mWindow.getWindowManager();
    }

2、DecorView和contentView的关系

DecorView是窗口最顶层的视图。在Activity#onCreate()中的setContentView()中,由应用theme/feature特性,找到对应的窗口修饰布局文件,窗口修饰布局文件中有一个id为content的控件即为mContentParent,建立mDecor和mContentParent之间的联系;而mContentParent是R.layout.layout_xx的父布局!

可以先看一个图:

2.1、Activity#setContentView()

一般在Activity.onCreate中通过setContentView来加载我们的自定义布局文件R.layout.xxx;setContentView又是通过window来实现的。

getWindow()返回一个window对象,在activity.attach()中实例化。Window的唯一实现子类是PhoneWindow,PhoneWindow#setContentView有两种重载方式,一种是layoutResID,一种是view

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

    public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }
    
    public Window getWindow() {
        return mWindow; //在activity.attach()中实例化
    }

2.1.1、setContentView(view)

分两步走

  • installDecor():得到mDecor、mContentParent
  • mContentParent.addView(view):设置自定义view
@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里包含了我们自定义View
        }
    }

2.1.2、setContentView(layoutResId)

也是分两步走

  • installDecor():得到mDecor、mContentParent
  • mContentParent.addView(view):设置自定义view

可以对比看出setContentView(layoutResId)和setContentView(view)的区别,layoutResId需要通过LayoutInflater.inflate来加载布局, mLayoutInflater.inflate(layoutResID, mContentParent); 其实最终也是通过viewGroup.addView(view, params)来实现的。

@Override
    public void setContentView(int layoutResID) {
        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);
        }
    }

LayoutInflater#inflate()

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
    
     public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
 
        final XmlResourceParser parser = res.getLayout(resource);
            return inflate(parser, root, attachToRoot);

    }
    
     public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
                    rInflate(parser, root, inflaterContext, attrs, false);
            return result;
        }
    }
    
    void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
            else {
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }
    }

也就是说setContentView(layoutResId)和setContentView(view)的本质是一样的,都是mContentParent.add(自定义View)。mContentParent是个啥东东呢?decorView又在哪里呢? 现在来分析下installDecor()

2.2、PhoneWindow#installDecor()

1、如果mDecor为空,则new一个DecorView对象,DecorView继承自FrameLayout,是所有布局的根布局。

2、如果mContentParent为空,则会根据theme/feature得到一个系统布局layoutResource,在layoutResource找到R.id.content,作为mContentParent。

3、同时mDecor将作为layoutResource的父View

也就是说mDecor包含layoutResource,layoutResource包含mContentParent,mContentParent包含我们的自定义View布局。

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

PhoneWindow#generateDecor()

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

PhoneWindow#generateLayout()

  public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
    
 protected ViewGroup generateLayout(DecorView decor) {
    // Inflate the window decor.
        int layoutResource;

        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
        }
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        return contentParent;
    }

DecorView#onResourceLoaded()

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {

        final View root = inflater.inflate(layoutResource, null);
    
        } else {
            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    }

R.layout.screenSimple

<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和DecorView进行绑定

前面我们说了在activity.attach()中得到了activity的window对象和windowManager对象;

通过PhoneWindow#setContentView(),我们自定义的View布局也成功的绑定到了DecorView这个根布局上面;

现在还差window和DecorView进行绑定。

Window和decorView的绑定是在ActivityThread#performResumeActivity()中,activity#onResume()之后进行绑定的。

ActivityThread#handlerResumeActivity()

  • wm.addView(decor, l); windowManger把decorView添加到这个窗口里

  • wm.updateViewLayout(decor, l); windowManger更新窗口里的View

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
     
        r = performResumeActivity(token, clearHide, reason); //执行activity.onResume

        if (r != null) {
            final Activity a = r.activity;
        
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow(); //获取window对象
                View decor = r.window.getDecorView(); //获取decorView
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager(); //获取windowManager对象
                WindowManager.LayoutParams l = r.window.getAttributes(); //获取windowManger.layoutParams
                a.mDecor = decor;
             
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);// 如果mDecor还没有添加到window中,则添加
                    } else {
                        a.onWindowAttributesChanged(l);
                    }
                }

            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
       
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
        }
    }

WindowManger是用来管理Window对象的,是一个接口类,继承自ViewManager。

WindowManagerImpl实现了WindowManager接口,通过代理类WindowManagerGlobal来实现对View的操作。

ViewManager.java

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

WindowManager.java

public interface WindowManager extends ViewManager {
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
    }
}

WindowManagerImpl.java

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    private IBinder mDefaultToken;

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

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }
}

WindowManagerGlobal.java

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

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
      
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
       
       ViewRootImpl root;
       View panelParentView = null;
       
        for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
                
            root = new ViewRootImpl(view.getContext(), display); //得到了viewRootImpl
            view.setLayoutParams(wparams);

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

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } 
        }
    }

总结

activity虽然给我们展示了用户界面,但是对view的操作却是由window来实现的

activity -> window -> view

参考:

Android View源码解读:浅谈DecorView与ViewRootImpl

猜你喜欢

转载自blog.csdn.net/ZHENZHEN9310/article/details/86487937
今日推荐