文末本文转载自微信公众号「 Android开发编程」,作者 Android开发编程。转载本文请联系 Android开发编程公众号。
前言
Activity和window,DecorView ,viewRoot是什么关系
今天我们就来讲解下,这样你在面试时候,游刃有余;
一、基本概念介绍
1、Activity
- Activity负责控制生命周期和处理事件;
- 负责统筹视图的添加与显示,以及通过一些回调方法与Window和View进行交互;
- 一个Activity包含一个Window,真正控制视图的是Window,Window才是真正代表一个窗口;
- 统筹视图的添加与显示,通过回调与Window和View进行交互;
2、Window
- Window是视图的承载者,是一个抽象类;
- Activity中持有的实际上是Window的子类PhoneWindow;
- Window通过WindowManager加载了一个DecorView到Window中,并将DecorView交给了ViewRoot;
3、DecorView
- DecorView的父类是FrameLayout,是Android View树的根节;
- 内部包含一个竖直方向的LinearLayout,它有上下三个部分,上面是个ViewStub,延迟加载的视图(ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。setContentView所设置的布局文件其实就是被加到内容栏之中的;
- ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
- ViewGroup rootView = (ViewGroup) content.getChildAt(0)
4、ViewRoot
- 控制View的事件处理和逻辑处理;
- ViewRoot子类是ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成;
- ViewRoot并不属于View树的一部分。从源码实现上来看,它既非View的子类,也非View Group,但它实现了ViewParent接口,这让它可以作为View的名义上的父视图;
- RootView继承了Handler类,可以接收事件并分发;
- Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的;
二、DecorView的创建整个流程详解
1、attach
Activity的setContentView()开始
- public void setContentView(@LayoutRes int layoutResID) {
- getWindow().setContentView(layoutResID);
- initWindowDecorActionBar();
- }
可以看到实际上是交给Window来装载视图的;
- 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) {
- ..................................................................
- mWindow = new PhoneWindow(this, window);//创建一个Window对象
- mWindow.setWindowControllerCallback(this);
- mWindow.setCallback(this);//设置回调,向Activity分发点击或状态改变等事件
- mWindow.setOnWindowDismissedCallback(this);
- .................................................................
- mWindow.setWindowManager(
- (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
- mToken, mComponent.flattenToString(),
- (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//给Window设置WindowManager对象
- ....................................................................
- }
在Activity的attach方法中生成了PhoneWindow的实例;
有了Window对象,接下来就将DecorView加载到Window中;
2、setContentView
- public void setContentView(int layoutResID) {
- if (mContentParent == null) {//mContentParent为空,创建一个DecroView
- installDecor();
- } else {
- mContentParent.removeAllViews();//mContentParent不为空,删除其中的View
- }
- mLayoutInflater.inflate(layoutResID, mContentParent);//为mContentParent添加子View,即Activity中设置的布局文件
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
- cb.onContentChanged();//回调通知,内容改变
- }
- }
mContentParent就是ContentView所对应的的FrameLayout;
Activity的setContentView的流程大致可以总结为:
Activity首先在Attach方法中生成了PhoneWindow的实例;
在setContentView中直接交给Window来装载视图,先在PhoneWindow中创建了一个DecroView;
其中创建的过程中可能根据Theme不同,加载不同的布局格式,即Activity中设置的布局;
3、installDecor
- private void installDecor() {
- if (mDecor == null) {
- mDecor = generateDecor(); //生成DecorView
- mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- mDecor.setIsRootNamespace(true);
- if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
- mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
- }
- }
- if (mContentParent == null) {
- mContentParent = generateLayout(mDecor); // 为DecorView设置布局格式,并返回mContentParent
- ...
- }
- }
- }
- protected DecorView generateDecor() {
- return new DecorView(getContext(), -1);
- }
很简单,创建了一个DecorView;
再看generateLayout;
4、generateLayout
- protected ViewGroup generateLayout(DecorView decor) {
- // 从主题文件中获取样式信息
- TypedArray a = getWindowStyle();
- ...................
- if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
- requestFeature(FEATURE_NO_TITLE);
- } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
- // Don't allow an action bar if there is no title.
- requestFeature(FEATURE_ACTION_BAR);
- }
- ................
- // 根据主题样式,加载窗口布局
- int layoutResource;
- int features = getLocalFeatures();
- // System.out.println("Features: 0x" + Integer.toHexString(features));
- if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
- layoutResource = R.layout.screen_swipe_dismiss;
- } else if(...){
- ...
- }
- View in = mLayoutInflater.inflate(layoutResource, null);//加载layoutResource
- //往DecorView中添加子View,即文章开头介绍DecorView时提到的布局格式,那只是一个例子,根据主题样式不同,加载不同的布局。
- decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- mContentRoot = (ViewGroup) in;
- ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// 这里获取的就是mContentParent
- if (contentParent == null) {
- throw new RuntimeException("Window couldn't find content container view");
- }
- if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
- ProgressBar progress = getCircularProgressBar(false);
- if (progress != null) {
- progress.setIndeterminate(true);
- }
- }
- if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
- registerSwipeCallbacks();
- }
- // Remaining setup -- of background and title -- that only applies
- // to top-level windows.
- ...
- return contentParent;
- 先从主题中获取样式,然后根据样式;
- 加载对应的布局到DecorView中,然后从中获取mContentParent;
- 获得到之后,可以回到上面的代码,为mContentParent添加View,即Activity中的布局;
5、DecorView的显示
将DecorView建立起来,通过setContentView设置的界面,如何在onResume后对用户可见,需要从ActivityThread说起;
- private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
- //就是在这里调用了Activity.attach(),接着调用了Activity.onCreate()和Activity.onStart()生命周期,
- //但是由于只是初始化了mDecor,添加了布局文件,还没有把
- //mDecor添加到负责UI显示的PhoneWindow中,所以这时候对用户来说,是不可见的
- Activity a = performLaunchActivity(r, customIntent);
- ......
- if (a != null) {
- //这里面执行了Activity.onResume()
- handleResumeActivity(r.token, false, r.isForward,
- !r.activity.mFinished && !r.startsNotResumed);
- if (!r.activity.mFinished && r.startsNotResumed) {
- try {
- r.activity.mCalled = false;
- //执行Activity.onPause()
- mInstrumentation.callActivityOnPause(r.activity);
- }
- }
- }
- }
重点看下handleResumeActivity(),在这其中,DecorView将会显示出来,同时重要的一个角色;ViewRoot也将登场;
6、handleResumeActivity
- final void handleResumeActivity(IBinder token, boolean clearHide,
- boolean isForward, boolean reallyResume) {
- //这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
- ActivityClientRecord r = performResumeActivity(token, clearHide);
- if (r != null) {
- final Activity a = r.activity;
- if (r.window == null && !a.mFinished && willBeVisible) {
- r.window = r.activity.getWindow();
- View decor = r.window.getDecorView();
- //decor对用户不可见
- decor.setVisibility(View.INVISIBLE);
- ViewManager wm = a.getWindowManager();
- WindowManager.LayoutParams l = r.window.getAttributes();
- a.mDecor = decor;
- l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
- if (a.mVisibleFromClient) {
- a.mWindowAdded = true;
- //被添加进WindowManager了,但是这个时候,还是不可见的
- wm.addView(decor, l);
- }
- if (!r.activity.mFinished && willBeVisible
- && r.activity.mDecor != null && !r.hideForNow) {
- //在这里,执行了重要的操作,使得DecorView可见
- if (r.activity.mVisibleFromClient) {
- r.activity.makeVisible();
- }
- }
- }
- }
- }
当我们执行了Activity.makeVisible()方法之后,界面才对我们是可见的;
- void makeVisible() {
- if (!mWindowAdded) {
- ViewManager wm = getWindowManager();
- wm.addView(mDecor, getWindow().getAttributes());//将DecorView添加到WindowManager
- mWindowAdded = true;
- }
- mDecor.setVisibility(View.VISIBLE);//DecorView可见
- }
- 到此DecorView便可见,显示在屏幕中;
- 但是在这其中,wm.addView(mDecor, getWindow().getAttributes());
- 起到了重要的作用,因为其内部创建了一个ViewRootImpl对象,负责绘制显示各个子View;
- 具体来看addView()方法,因为WindowManager是个接口,具体是交给WindowManagerImpl来实现的;
7、addView
- public final class WindowManagerImpl implements WindowManager {
- private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- ...
- @Override
- public void addView(View view, ViewGroup.LayoutParams params) {
- mGlobal.addView(view, params, mDisplay, mParentWindow);
- }
- }
- 交给WindowManagerGlobal 的addView()方法去实现;
- public void addView(View view, ViewGroup.LayoutParams params,
- Display display, Window parentWindow) {
- final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
- ......
- synchronized (mLock) {
- ViewRootImpl root;
- //实例化一个ViewRootImpl对象
- root = new ViewRootImpl(view.getContext(), display);
- view.setLayoutParams(wparams);
- mViews.add(view);
- mRoots.add(root);
- mParams.add(wparams);
- }
- ......
- try {
- //将DecorView交给ViewRootImpl
- root.setView(view, wparams, panelParentView);
- } catch (RuntimeException e) {
- }
- }
- 看到其中实例化了ViewRootImpl对象,然后调用其setView()方法;
- 其中setView()方法经过一些列折腾,最终调用了performTraversals()方法,完成绘制,最终界面才显示出来;
总结
- Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图;
- DecorView就是个顶层视图,是所有View的最外层布局;
- ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互;