Android高手进阶之Activity.setContentView渲染流程详解
前言
setContentView(R.layout.activity_main)这么简简单单的手进一段代码做了事情可不简单;
接下来我们会跟着源码大概走一遍这个方法,一起总结下
一、渲染详解DecorView的流程创建

Activity.setContentView
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); //最终调用mWindow的setContentView方法 initWindowDecorActionBar(); }getWindow返回的是mWindow, mWindow在Activity的手进attach方法里被赋值,是个PhoneWindow对象。
(PhoneWindow是渲染详解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, ActivityConfigCallback activityConfigCallback) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window, activityConfigCallback); ... mWindow.setWindowManager( //设置WindowManager (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); }PhoneWindow.setContentView
几个关键变量
1.mDecor 是Window的最顶层的View,是流程个FrameLayout。
2.mContentParent 是手进用来真正装载Activity传入的布局文件的容器,本身是渲染详解个ViewGroup。
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(); //如果mContentParent为空,则执行installDecor方法 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); //否则remove掉mContentParent的手进所有子view } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); //将activity传入的布局文件加载到mContentParent里 } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }PhoneWindow.installDecor
private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { mDecor = generateDecor(-1); //如果之前没有创建,直接创建一个 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); //将PhoneWindow传递给DecorView } if (mContentParent == null) { mContentParent = generateLayout(mDecor); //赋值mContentParent ... }PhoneWindow.installDecor
protected DecorView generateDecor(int featureId) { // System process doesnt have application context and in that case we need to directly use // the context we have. Otherwise we want the application context,渲染详解 so we dont cling to the // activity. Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else { context = new DecorContext(applicationContext, getContext()); if (mTheme != -1) { context.setTheme(mTheme); } } } else { context = getContext(); } return new DecorView(context, featureId, this, getAttributes()); //创建DecorView }PhoneWindow.generateLayout
这一步是挑选合适的DecorView布局文件并将其添加大盘DecorView,同时给mContentParent赋值。流程
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle(); ...... // Inflate the window decor. //根据不同的免费源码下载手进features来选择DecorView的布局 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; setCloseOnSwipeEnabled(true); } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //挑选出来的布局添加到DecorView中 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //从DecorView找出id为com.android.internal.R.id.content的容器,提供给Activity使用。渲染详解 if (contentParent == null) { throw new RuntimeException("Window couldnt find content container view"); } ...... return contentParent; }R.layout.test
这一步纯粹是流程为了看下DecorView布局文件长啥样
<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" /> <!--真正存放Activity布局的容器--> <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>DecorView.onResourcesLoaded
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { if (mBackdropFrameRenderer != null) { loadBackgroundDrawablesIfNeeded(); mBackdropFrameRenderer.onResourcesLoaded( this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), getCurrentColor(mNavigationColorViewState)); } mDecorCaptionView = createDecorCaptionView(inflater); final View root = inflater.inflate(layoutResource, null); //解析出布局文件 if (mDecorCaptionView != null) { if (mDecorCaptionView.getParent() == null) { addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { // Put it below the color views. addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //添加到DecorView } mContentRoot = (ViewGroup) root; //mContentRoot保存的是整个跟布局容器 initializeElevation(); } 
二、DecorView绘制到屏幕
Activity 执行到 onCreate 时并不可见,只有执行完 onResume 之后 Activity 中的内容才是屏幕可见状态。onCreate 阶段只是初始化了 Activity 需要显示的内容,而在 onResume 阶段才会将 PhoneWindow 中的 DecorView 真正的绘制到屏幕上。
在ActivityThread的handleResumeActivity方法中,调用WindowManager将decor作为窗口添加到 WMS 。
@Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { ...... if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); //通过WindowManager将decor添加到WMS } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } } ......实现WindowManager接口的是WindowManagerImpl类,从WINDOW_SERVICE注册时也能看出来。
registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); }});WindowManagerImpl.addView调用mGlobal.addView方法,mGlobal是WindowManagerGlobal类型变量。
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }WindowManagerGlobal.addView最终调的是ViewRootImpl的b2b信息网setView方法
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... ViewRootImpl root; View panelParentView = null; synchronized (mLock) { ...... 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); //最终,调的是ViewRootImpl的setView方法 } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }ViewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { ...... int res; /* = WindowManagerImpl.ADD_OKAY; */ // 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(); //调用此方法后 ViewRootImpl 所关联的 View 也执行 measure - layout - draw 操作,确保在 View 被添加到 Window 上显示到屏幕之前,已经完成测量和绘制操作。 ...... try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); // mWindowSession 的 addToDisplay 方法将 View 添加到 WMS 中。 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } } ...... } }WindowSession实例获取,是IWindowSession类型,通过Binder机制调用System 进程中的 Session实现。
public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } }addToDisplay真正实现。
@Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel, outInsetsState); }至此,Window 已经成功的被传递给了 WMS。剩下的工作就全部转移到系统进程中的 WMS 来完成最终的添加操作。
三、触摸事件处理
ViewRootImpl 中的 setView 方法中,除了调用 IWindowSession 执行跨进程添加 View 之外,还有一项重要的操作就是设置输入事件的处理:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ...... // Set up the input pipeline. CharSequence counterSuffix = attrs.getTitle(); mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix); InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, "aq:native-pre-ime:" + counterSuffix); ...... }最终会经过ViewPostImeInputStage的onProcess处理
final class ViewPostImeInputStage extends InputStage { public ViewPostImeInputStage(InputStage next) { super(next); } @Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { return processTrackballEvent(q); } else { return processGenericMotionEvent(q); } } }processPointerEvent方法,调用mView的dispatchPointerEvent 分发事件。源码下载mView是DecorView对象
private int processPointerEvent(QueuedInputEvent q) { ...... boolean handled = mView.dispatchPointerEvent(event); ...... return handled ? FINISH_HANDLED : FORWARD; }dispatchPointerEvent是View实现的,最终调的是dispatchTouchEvent方法。
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }DecorView.dispatchTouchEvent最终调用PhoneWindow的Callback分发事件。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }PhoneWindow的Callback是在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) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); //将Activity对象传递给PhoneWindow ...... }下面就回到了我们熟悉的Activity的dispatchTouchEvent方法:
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { //调用PhoneWindow的superDispatchTouchEvent方法 return true; } return onTouchEvent(ev); }PhoneWindow.superDispatchTouchEvent,归根结底还是调的DecorView的superDispatchTouchEvent方法
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }DecorView.superDispatchTouchEvent,调用ViewGroup的dispatchTouchEvent方法。
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }总结
1.整个过程Activity参与度很低,基本靠PhoneWindow实现。
2.onCreate阶段创建了DecorView,onResume阶段将DecorView添加到WMS并展示,ViewRootImple对象也是onResume阶段创建的,所以也解释了onCreate阶段子线程加载view并不会报错。
3.ViewRootImpl 的 setView 方法中主要完成两件事情:View 渲染(requestLayout)以及接收触屏事件。
4.一个 Activity 中有一个 window,也就是 PhoneWindow 对象,每一个 PhoneWindow 对应一个 ViewRootImple 对象。
本文转载自微信公众号「Android开发编程」

相关文章
装机新手必看!以装机盘为工具的装机教程大揭秘!(教你一步步轻松装机,让电脑焕然一新!)
摘要:对于不少新手来说,装机可是一件令人头疼的事情。从硬件选购到驱动安装,各个环节都让人不知所措。但现在,有了装机盘的帮助,这一切将变得轻松简单。本文将以装机盘为主题,为大家详细介绍如何...2025-11-04使用HDDScan教程(掌握HDDScan工具,保护您的硬盘安全与稳定)
摘要:在数字化时代,我们越来越依赖于电脑和硬盘存储数据。然而,硬盘问题可能会给我们带来巨大的麻烦,包括数据丢失和系统崩溃等。了解如何使用可靠的工具检测和修复硬盘问题至关重要。本文将为您提...2025-11-04联想电脑系统从U盘启动教程(简明易懂的操作步骤带你轻松实现)
摘要:在某些情况下,我们可能需要从U盘启动电脑系统,这种方式可以帮助我们修复电脑故障、安装操作系统等。而联想电脑作为一款常见的品牌,今天我们就来分享一下联想电脑系统如何从U盘启动的详细操...2025-11-04微星GP72VR散热性能评测(GP72VR散热怎么样?深度解析微星游戏笔记本的散热设计与性能表现)
摘要:作为一款专为游戏而设计的笔记本电脑,微星GP72VR以其高性能和出色的游戏体验而备受玩家的喜爱。然而,高性能往往伴随着高功耗和散热难题,因此散热性能对于一台游戏笔记本来说尤为重要。...2025-11-04OPPOR9s最新版本(OPPOR9s新版本发布,全新突破创新体验)
摘要:随着科技的不断进步,智能手机已经成为我们日常生活中不可或缺的一部分。OPPOR9s作为OPPO旗下最新一代旗舰机型,备受期待。本文将从外观设计、摄影功能、性能表现等多个方面,详细介...2025-11-04- 摘要:牛仔裤是时尚界的经典单品,然而,随着时间的流逝和频繁的穿着洗涤,牛仔裤往往会出现褪色问题。对于喜欢穿牛仔裤的人来说,保持牛仔裤颜色鲜艳是一项重要任务。本文将介绍一些科学的护理方法,...2025-11-04


最新评论