从源码角度理解Activity 与 Window、PhoneWindow、DecorView 四者之间的关系

从Activity的源码分析,我们得知,每个activity都持有一个Window对象:

public class Activity extends ContextThemeWrapper{
	@UnsupportedAppUsage
	private Window mWindow;
}

但是Window是一个抽象类,他只有唯一的实现类PhoneWindow,本质上来讲,activity持有的Window对象就是一个PhoneWindow对象。

Window对象的创建则是在Activity的attach方法里面完成的:

@UnsupportedAppUsage
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, IBinder assistToken) {
   ......……
    //完成Window 对象的实现类的创建
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    ......
    //Activity的UI线程的获取
    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    ......
    //初始化且设置windowManager各参数
    mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, 
            mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    //mParent:Activity对象
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mWindow.setColorMode(info.colorMode);
	......
}

PhoneWindow 中持有一个 Android 中非常重要的一个 View 对象 DecorView(装饰),它在 PhoneWindow 中的定义如下:

public class PhoneWindow extends Window{
	// This is the top-level view of the window, containing the window decor:这是一个顶级的窗口View,包含DecorView
	private DecorView mDecor; 
}

而上面的DecorView本质上是这样的:

public class DecorView extends FrameLayout {		
}

可以看出DecorView 是一个继承于FrameLayout的ViewGroup类型的View。
因此,串起来理解就是:

每一个 Activity 持有一个 PhoneWindow 的对象,而一个 PhoneWindow 对象持有一个 DecorView 的实例,Activity 中 View 相关的操作和具体实现,大都是通过 DecorView 来完成。

但PhoneWindow对View来说更多是扮演容器的角色,而真正完成把一个 DecorView ,作为窗口添加到Window的过程是由 WindowManager(WMS) 来完成的。而WindowManager接口 的具体实现是 WindowManagerImpl。

这就涉及到Activity的启动过程,这里不做过多的扩散,仅仅丢出与本文相关的代码:

//ActivityThread
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ``````
    //获取WindowManagerService的Binder引用(proxy端)。
    WindowManagerGlobal.initialize();
    //创建activity,调用attach方法,然后调用Activity的onCreate,onStart,onResotreInstanceState方法
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        ``````
        //会调用Activity的onResume方法.
        handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        ``````
    }
}

handleResumeActivity()方法具体实现如下:

//ActivityThread
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    //把activity数据记录更新到ActivityClientRecord
    ActivityClientRecord r = mActivities.get(token);
    r = performResumeActivity(token, clearHide, reason);
    if (r != null) {
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);//不可见
            ViewManager wm = a.getWindowManager();
            ......
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                a.mWindowAdded = true;
                //把decor添加到窗口上(敲黑板,划重点,把DecorView添加到Window就是在这里实现的)
                wm.addView(decor, l);
            }
        }
        //屏幕参数发生了改变
      	......
        if (r.activity.mVisibleFromClient) {
            //已经成功添加到窗口上了(绘制和事件接收),设置为可见
            r.activity.makeVisible();
        }
        ......
    }
}

那么,DecorView 如何与 Activity 关联起来的?这就需要我们常用到的一个纽带:setContentView()方法

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

上面setContentView()方法里面,getWindow()方法返回一个mWindow对象,实质上是一个PhoneWindow对象,然后调用setContentView()方法,把我们的布局设置给PhoneWindow对象,而PhoneWindow持有一个DecorView 实例,decorView 本身是一个 FrameLayout,当 decorView 接受到来自 Activity 传递过来的布局 id 后,通过 inflater,把布局资源 id 转换为一个 View,然后把这个布局 View 添加在自身中。

流程如下:
首先,我们开发自定义的XXXActivity的onCreate()方法里面通常是这样的:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
}

然后setContentView()方法在XXXActivity的父类Activity(根Activity)里面是这样的:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow().setContentView(layoutResID);负责把我们的布局给PhoneWindow持有的DecorView 实例, 当 decorView 接受到来自 Activity 传递过来的布局 id 后,通过 inflater,把布局资源 id 转换为一个 View,然后把这个布局 View 添加在自身中。
initWindowDecorActionBar()方法主要实现ActionBar功能。

而根Activity持有的PhoneWindow对象getWindow()的setContentView()方法如下:

@Override    
public void setContentView(int layoutResID) {
	if (mContentParent == null) {
		installDecor();//初始化 id 为layoutResID的根布局,将其赋值给 mContentParent
	} else {
		mContentParent.removeAllViews();
	}
	//把 Activity 中指定的布局 id 最终 inflate 到 mContentParent 中
	mLayoutInflater.inflate(layoutResID, mContentParent);
	final Callback cb = getCallback();
	if (cb != null && !isDestroyed()) {
		cb.onContentChanged();
	}
}

这个方法就是最终发生关键作用的地方——将DecorView与 Activity 关联起来的核心所在,执行完该方法后,从 Activity 传递而来的布局资源 id 最终就会添加到 decorView 中。这里的 installDecor 方法很重要,通过该方法做了很多初始化相关的操作。
总的来说就是:
setContentView()起到了联系Activity和DecorView的作用:将我们的xml布局通过 inflater,把布局资源 id 转换为一个 View,然后把这个布局 View 添加到窗口自身(亦即DecorView)中;然后再由DecorView来具体实现更详细的功能,如View 的渲染绘制,View 事件分发等。

View 的渲染绘制详见《View的工作原理》
View 的事件分发详见《一图详解Android View的事件分发机制》

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值