android应用层view绘制流程之decorview与viewrootimpl_第1页
android应用层view绘制流程之decorview与viewrootimpl_第2页
android应用层view绘制流程之decorview与viewrootimpl_第3页
android应用层view绘制流程之decorview与viewrootimpl_第4页
android应用层view绘制流程之decorview与viewrootimpl_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Android 应用层 View 绘制流程之 DecorView 与 ViewRootImpl 概述 一直对 Android 中 View 的整个绘制流程不是很了解, View 是怎么添加到 Activity 当中去 的?当 View 中的内容发生改变的时候是怎样执行界面的刷新的?因此,今天准备从源码 的角度来对 View 的整个绘制流程来进行分析,源码基于 API25。由于篇幅限制,这篇文章 只分析顶层视图 DecorView 的显示逻辑,具体的 View 树绘制三部曲:measure,layout,draw 将在下篇博文进行深入剖析。 从 Activity 的 setContentView 方法说起 我们都知道给 Activity 设置布局通常就是调用其 setContentView 方法,这个方法有几个重 载的方法,如下所示: public void setContentView(LayoutRes int layoutResID) getWindow().setContentView(layoutResID); initWindowDecorActionBar(); public void setContentView(View view) getWindow().setContentView(view); initWindowDecorActionBar(); public void setContentView(View view, ViewGroup.LayoutParams params) getWindow().setContentView(view, params); initWindowDecorActionBar(); 从上面的三个方法可以看出其均调用了 getWindow()的相对应的方法,我们来看 getWindow()方法: public Window getWindow() return mWindow; 可以看出这个方法返回的是一个 Window 类型的变量 mWindow,那么这个 mWindow 肯定 是在 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) attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); mWindow 是一个 PhoneWindow 类型的变量,其实我们通过查看抽象类 Window 最开始的 简介可以知道,PhoneWindow 是 Window 的唯一子类! /* * Abstract base class for a top-level window look and behavior policy. An * instance of this class should be used as the top-level view added to the * window manager. It provides standard UI policies such as a background, title * area, default key processing, etc. * * The only existing implementation of this abstract class is * android.view.PhoneWindow, which you should instantiate when needing a * Window. */ public abstract class Window 接下来查看 PhoneWindow 的 setContentView 方法,跟 Activity 对应,也有三个重载的方法: 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 mContentParentExplicitlySet = true; Override public void setContentView(View view) setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT); Override public void setContentView(View view, ViewGroup.LayoutParams params) / 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) 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 mContentParentExplicitlySet = true; 对 setContentView(View view,ViewGroup.LayoutParams)方法进行分析: 首先判断 mContentParent 是否为 null,如果为 null 的话就执行方法 installDecor,这个 mContentParent 是一个 ViewGroup 类型,这个方法如下所示: private void installDecor() mForceDecorInstall = false; if (mDecor = null) mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted else mDecor.setWindow(this); if (mContentParent = null) mContentParent = generateLayout(mDecor); . generateDecor 用于产生 mDecor,mDecor 是 DecorView 类型,是整个 Activity 的顶层视图, DecorView 是 FrameLayout 的子类,有兴趣的可以看看 DecorView 源码,这里只是给个结 论。 然后是 generateLayout 方法,这个方法很长,看关键代码: ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent = null) throw new RuntimeException(“Window couldnt find content container view“); return contentParent; 从上述代码可以看出,上面讲到的 mContentParent 是顶层视图 mDecor 中的一个子 View, 这个子 View 的 id 为: /* * The ID that the main layout in the XML layout file should have. */ public static final int ID_ANDROID_CONTENT = ernal.R.id.content; 因此,执行了 installDecor 方法之后就得到了 mDecor 和 mContentParent,然后是一句很关 键的代码,如下所示: mContentParent.addView(view, params); 通过这句代码就把我们在 Activity 当中设置的布局视图加入了 mContentParent 里面。 层次关系为:DecorView contentParent Activity 中的布局。 由于 mContentParent 是 ViewGroup 类型,查看 ViewGroup#addView 方法,如下所示: /* * Adds a child view with the specified layout parameters. * * Note: do not invoke this method from * link #draw(android.graphics.Canvas), link #onDraw(android.graphics.Canvas), * link #dispatchDraw(android.graphics.Canvas) or any related method. * * param child the child view to add * param index the position at which to add the child or -1 to add last * param params the layout parameters to set on the child */ public void addView(View child, int index, LayoutParams params) if (DBG) System.out.println(this + “ addView“); if (child = null) throw new IllegalArgumentException(“Cannot add a null child view to a ViewGroup“); / addViewInner() will call child.requestLayout() when setting the new LayoutParams / therefore, we call requestLayout() on ourselves before, so that the childs request / will be blocked at our level requestLayout(); invalidate(true); addViewInner(child, index, params, false); 这个 addView 方法最后会调用 requestLayout()和 invalidate()方法,这两个方法会导致整个 View 树进行重新绘制,这样就把我们在 Activity 当中自定义的布局文件给显示出来了! 整个过程总结如下: Activity.setContentView - PhoneWindow.setContentView -初始化 PhoneWindow 中的 mDecor 和 mContentParent - 把 Activity 当中的布局视图加入 mContentParent - 导致整个 View 树进行重新绘制,从而把布局文件显示出来! DecorView 是怎么显示出来的 前面说了 DecorView 是整个 Activity 的顶层视图,那么这个 DecorView 是怎么显示出来了 的呢?主要实现过程在 ActivityThread 的 handleResumeActivity 方法里面,如下所示: final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) ActivityClientRecord r = mAivities.get(token); if (!checkAndUpdateLifecycleSeq(seq, r, “resumeActivity“) return; / If we are getting ready to gc after going to the background, well / we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true; / TODO Push resumeArgs into the activity for consideration r = performResumeActivity(token, clearHide, reason); if (r != null) final Activity a = r.activity; if (localLOGV) Slog.v( TAG, “Resume “ + r + “ started activity: “ + a.mStartedActivity + “, hideForNow: “ + r.hideForNow + “, finished: “ + a.mFinished); final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; / If the window hasnt yet been added to the window manager, / and this guy didnt finish itself or start another activity, / then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) try willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( a.getActivityToken(); catch (RemoteException e) throw e.rethrowFromSystemServer(); if (r.window = null View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) a.mWindowAdded = true; r.mPreserveWindow = false; / Normally the ViewRoot sets up callbacks with the Activity / in addView-ViewRootImpl#setView. If we are instead reusing / the decor view we have to notify the view root that the / callbacks ay have changed. ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) impl.notifyChildRebuilt(); if (a.mVisibleFromClient wm.addView(decor, l); 如上述方法所示,首先从 ActivityThread 保存的 mActivities 列表里面得到一个 ActivityClientRecord 对象,这个对象保存了 Activity 的一些信息。 得到 Activity 之后调用 View decor = r.window.getDecorView();方法得到顶层视图 DecorView,这个视图前面说过是保存在 PhoneWindow 里面,也就是一个 Activity 对应一 个 PhoneWindow,从而对应一个 DecorView。 然后是调用 ViewManager wm = a.getWindowManager();方法得到 Activity 当中的 WindowManager 对象,那为什么返回的是 ViewManager 对象呢?查看 WindowManager 接 口,如下所示,发现 WindowManager 是继承自 ViewManager 接口的。 public interface WindowManager extends ViewManager ViewManager 接口如下所示,从注释可以看出这个接口主要是用来往 Activity 当中添加或 者移除 View。 /* Interface to let you add and remove child views to an Activity. To get an instance * of this class, call link android.content.Context#getSystemService(java.lang.String) Context.getSystemService(). */ public interface ViewManager /* * Assign the passed LayoutParams to the passed View and add the view to the window. * Throws link android.view.WindowManager.BadTokenException for certain programming * errors, such as adding a second view to a window without removing the first view. * Throws link android.view.WindowManager.InvalidDisplayException if the window is on a * secondary link Display and the specified display cant be found * (see link android.app.Presentation). * param view The view to be added to this window. * param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); 得到 Activity 当中的 WindowManager 之后,调用 wm.addView(decor, l);方法,就把 DecorView 加入了 WindowManager。我们知道 WindowManager 只是一个接口,具体的实 现类是 WindowManagerImpl,看下其 addView 方法: Override public void addView(NonNull View view, NonNull ViewGroup.LayoutParams params) applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); 发现这个 addView 方法其实是调用了成员变量 mGlobal 的 addVeiw 方法,mGlobal 是 WindowManagerGlobal 类型,我们来看下其 addView 方法,如下所示: 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); . / do this last because it fires off messages to start doing things try root.setView(view, wparams, panelParentView); catch (RException e) / BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) final int index = findViewLocked(view, false); if (index = 0) removeViewLocked(index, true); throw e; 通过上述代码可以发现顶层视图 DecorView 最终被加入到了 ViewRootImpl 里面,且应该 是在其 setView 方法里面执行了某些操作,导致 DecorView 被显示出来。这个方法比较长, 大家可以自行查看,在里面有一句关键的代码: / Schedule the first layout -before- adding to the window / manager, to make sure we do the relayout before receiving / any other events from the system. requestLayout(); requestLayout 方法如下: Override public void requestLayout() if (!mHandlingLayoutInLayoutRequest) checkThread(); mLayoutRequested = true; scheduleTraversals(); 在这个方法当中调用了 scheduleTraversals 方法,如下所示: void scheduleTraversals() if (!mTraversalScheduled) mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论