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

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

下面对各个细节进行解说

2、Activity和Window进行绑定

  • ActivityThread的mH(Handler对象)收到LAUNCH_ACTIVITY的message,执行handlerLaunchActivity

  • ActivityThread#handlerLaunchActivity():执行performLaunchActivity

  • 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);
    }

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

3、DecorView和contentView的关系

  • DecorView是窗口最顶层的视图。

  • 在Activity#onCreate()中的setContentView()中,根据应用theme/feature特性,找到对应的窗口修饰布局文件R.layout.screenSimple,即layoutResource,decorView.add(layoutResourceView)

  • layoutResource中findViewById(ID_ANDROID_CONTENT),即mContentParent

  • mContentParent.add(R.layou_xx),绑定自定义layout布局

  • 建立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

//Activity.java
  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()中实例化,是个phoneWindow对象
    }
3.1、setContentView(view)

分两步走

  • installDecor():得到mDecor、mContentParent
  • mContentParent.addView(view):设置自定义view
//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) { //默认为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
        }
    }
3.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()

3.3、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布局。

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

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

	public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;  
 	protected ViewGroup generateLayout(DecorView decor) {
        int layoutResource;
        layoutResource = R.layout.screen_simple;
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

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

DecorView#onResourceLoaded()

//DecorView.java
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        final View root = inflater.inflate(layoutResource, null); //加载layoutResource
       //decorView.add(layoutResourceView)
        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>

4、Window和DecorView进行绑定

前面我们说了在activity.attach()中实例化了window对象,并绑定了windowManager对象;

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

现在还差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的setView中,会执行requestLayout(),出发performTranversals,绘制整个view树

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
      
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
       ViewRootImpl root;
  
            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 {
            //viewRootImpl的setView中,会执行requestLayout(),绘制整个view树
                root.setView(view, wparams, panelParentView);
            } 
        }
    }

总结

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

activity -> window -> view

参考:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值