一、前言
这篇文章主要是理解Acitivty对应的视图是如何显示出来的, 每次使用对应的Activity 我们都会去使用 setContentView,今天我们将看下Android中View的显示原理。
setContentView原理是什么Activity在onResume之后才会显示的原因是什么windowManager在显示过程起到怎么样的一个作用
相关类简介
Window :它是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理。WindowManager:是一个接口类,继承自接口 ViewManager,从名称就知道它是用来管理Window的WindowManagerImpl: WindowManager的实现类,WindowManager会将具体的工作交由WMS来处理。 如果我们想要对Window(View)进行添加、更新和删除操作就可以使用WindowManager
注 Activity 系列framework 源码使用 android10 release 分支
frameworks/base/services/core/java/com/android/server/wm/
- WindowManagerService.java
- Session.java
frameworks/base/core/java/android/app/
- Activity.java
- PhoneWindow.java
- WindowManagerImpl.java
- ViewRootImpl.java
- WindowManager.java
- ViewManager.java
二、Activity显示原理
2.1 Activity:: setContentView
public void setContentView(@LayoutRes int layoutResID
) {
getWindow().setContentView(layoutResID
);
initWindowDecorActionBar();
}
2.2 Activity:: getWindow
返回了一个Window对象
private Window mWindow
;
public Window
getWindow() {
return mWindow
;
}
我们写下来看下Window是在Activity的什么时候进行初始化的
2.3 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
) {
attachBaseContext(context
);
mFragments
.attachHost(null
);
mWindow
= new PhoneWindow(this, window
, activityConfigCallback
);
mWindow
.setWindowControllerCallback(this);
mWindow
.setCallback(this);
mWindow
.setOnWindowDismissedCallback(this);
mWindow
.getLayoutInflater().setPrivateFactory(this);
if (info
.softInputMode
!= WindowManager
.LayoutParams
.SOFT_INPUT_STATE_UNSPECIFIED
) {
mWindow
.setSoftInputMode(info
.softInputMode
);
}
if (info
.uiOptions
!= 0) {
mWindow
.setUiOptions(info
.uiOptions
);
}
mUiThread
= Thread
.currentThread();
mWindow
.setWindowManager(
(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();
mCurrentConfig
= config
;
mWindow
.setColorMode(info
.colorMode
);
setAutofillOptions(application
.getAutofillOptions());
setContentCaptureOptions(application
.getContentCaptureOptions());
}
这个attach函数的调用时机我们在上文讲过
创建Activity对象创建Context对象准备Application对象attach上下文Activity onCreate
这个PhoneWindow是什么呢,Activity调用的ContentView实际上调用的是PhoneWindow的contentView
2.4 PhoneWindow::setContentView
@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
);
}
mContentParent
.requestApplyInsets();
final Callback cb
= getCallback();
if (cb
!= null
&& !isDestroyed()) {
cb
.onContentChanged();
}
mContentParentExplicitlySet
= true;
}
这里的mContentParent是一个ViewGroup,如果ViewGroup为空的话,就初始化decorView,如果不为空就会走 mLayoutInflater.inflate(layoutResID, mContentParent) 将生成的View放到ViewGroup里面
2.5 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);
}
if (mDecor
.getBackground() == null
&& mBackgroundFallbackDrawable
!= null
) {
mDecor
.setBackgroundFallback(mBackgroundFallbackDrawable
);
}
}
这里我们看到setContentView的具体作用,初始化一个DecorView,即初始化整个屏幕的页面布局,然后给我们的布局加入到mContentParent里面,到这里顶多建造了一个ViewTree的数据结构,页面还未显示出来,那么页面什么时候才会显示出来呢,我们继续往下看
2.6 PhoneWindow:: generateDecor()
protected DecorView
generateDecor(int featureId
) {
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());
}
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
}
DecorView其实是一个FrameLayout,是整个手机的RootView
2.7ActivityThread:: handleResumeActivity
@Override
public void handleResumeActivity(IBinder token
, boolean finalStateRequest
, boolean isForward
,
String reason
) {
final Activity a
= r
.activity
;
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();
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;
ViewRootImpl impl
= decor
.getViewRootImpl();
if (impl
!= null
) {
impl
.notifyChildRebuilt();
}
}
if (a
.mVisibleFromClient
) {
if (!a
.mWindowAdded
) {
a
.mWindowAdded
= true;
wm
.addView(decor
, l
);
} else {
a
.onWindowAttributesChanged(l
);
}
if (r
.activity
.mVisibleFromClient
) {
r
.activity
.makeVisible();
}
}
} else if (!willBeVisible
) {
if (localLOGV
) Slog
.v(TAG
, "Launch " + r
+ " mStartedActivity set");
r
.hideForNow
= true;
}
Looper
.myQueue().addIdleHandler(new Idler());
}
控制此Activity的主窗口是否可见。这仅适用于不打算显示UI本身,但不能在onResume()之前完成的特殊情况,因为它需要等待服务绑定等。将此设置为false可以防止在此期间显示UI。
我们知道页面显示是在onResume的生命周期回调后,我们的setContentView是在onCreate的时候调用的,那这期间做了什么事让UI显示出来的呢,主要是调用了上面这个函数。 这里通过 ViewManager将 DecorView加入,再通过 r.activity.makeVisible()使Activity可见(这只是触发了一次重绘),重要的是谁来启动,谁来管理 我们继续跟进下 wm.addView(decor, l);
ViewManager && WindowManager && WindowManagerImpl
public interface ViewManager
{
public void addView(View view
, ViewGroup
.LayoutParams params
);
public void updateViewLayout(View view
, ViewGroup
.LayoutParams params
);
public void removeView(View view
);
}
@SystemService(Context
.WINDOW_SERVICE
)
public interface WindowManager extends ViewManager {
}
public final class WindowManagerImpl implements WindowManager {
这里我们可以看到 WindowManagerImpl实际上是WindowManager 的实现类,上节中调用的 addView实际实现是有 WindowManagerImpl实现
2.8 WindowManagerImpl:: addView
@Override
public void addView(@NonNull View view
, @NonNull ViewGroup
.LayoutParams params
) {
applyDefaultToken(params
);
mGlobal
.addView(view
, params
, mContext
.getDisplay(), mParentWindow
);
}
2.9 WindowManagerGlobal::addView
public void addView(View view
, ViewGroup
.LayoutParams params
,
Display display
, Window parentWindow
) {
...
root
= new ViewRootImpl(view
.getContext(), display
);
view
.setLayoutParams(wparams
);
mViews
.add(view
);
mRoots
.add(root
);
mParams
.add(wparams
);
try {
root
.setView(view
, wparams
, panelParentView
);
} catch (RuntimeException e
) {
if (index
>= 0) {
removeViewLocked(index
, true);
}
throw e
;
}
}
}
2.10 ViewRootImpl::setView
public void setView(View view
, WindowManager
.LayoutParams attrs
, View panelParentView
) {
...
requestLayout();
try {
mOrigWindowType
= mWindowAttributes
.type
;
mAttachInfo
.mRecomputeGlobalAttributes
= true;
collectViewAttributes();
res
= mWindowSession
.addToDisplay(mWindow
, mSeq
, mWindowAttributes
,
getHostVisibility(), mDisplay
.getDisplayId(), mTmpFrame
,
mAttachInfo
.mContentInsets
, mAttachInfo
.mStableInsets
,
mAttachInfo
.mOutsets
, mAttachInfo
.mDisplayCutout
, mInputChannel
,
mTempInsets
);
setFrame(mTmpFrame
);
} catch (RemoteException e
) {
...
} finally {
if (restore
) {
attrs
.restore();
}
}
}
这部分代码比较长,我们看重要的两部分代码 requestLayout() 方法和 mWindowSession.addToDisplay
2.10.1ViewRootImpl::requestLayout
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest
) {
checkThread();
mLayoutRequested
= true;
scheduleTraversals();
}
}
ViewRootImpl:: scheduleTraversals
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled
) {
mTraversalScheduled
= true;
mTraversalBarrier
= mHandler
.getLooper().getQueue().postSyncBarrier();
mChoreographer
.postCallback(
Choreographer
.CALLBACK_TRAVERSAL
, mTraversalRunnable
, null
);
if (!mUnbufferedInputDispatch
) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这个callback会在下一个vsync信号来以后触发调用 mTraversalRunnable
ViewRootImpl::TraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
ViewRootImpl:: doTraversal()
void doTraversal() {
if (mTraversalScheduled
) {
mTraversalScheduled
= false;
mHandler
.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier
);
if (mProfile
) {
Debug
.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile
) {
Debug
.stopMethodTracing();
mProfile
= false;
}
}
}
ViewRootImpl:: performTraversals()
这个performTraversals就是真正实行绘制的
private void performTraversals() {
relayoutResult
= relayoutWindow(params
, viewVisibility
, insetsPending
);
...
performMeasure(childWidthMeasureSpec
, childHeightMeasureSpec
);
...
performLayout(lp
, mWidth
, mHeight
);
...
performDraw();
}
relayoutWindow 向WMS申请Service的Surface
2.10.2ViewRootImpl:: relayoutWindow
private int relayoutWindow(WindowManager
.LayoutParams params
, int viewVisibility
,
boolean insetsPending
) throws RemoteException
{
...
int relayoutResult
= mWindowSession
.relayout(mWindow
, mSeq
, params
,
(int) (mView
.getMeasuredWidth() * appScale
+ 0.5f),
(int) (mView
.getMeasuredHeight() * appScale
+ 0.5f), viewVisibility
,
insetsPending
? WindowManagerGlobal
.RELAYOUT_INSETS_PENDING
: 0, frameNumber
,
mTmpFrame
, mPendingOverscanInsets
, mPendingContentInsets
, mPendingVisibleInsets
,
mPendingStableInsets
, mPendingOutsets
, mPendingBackDropFrame
, mPendingDisplayCutout
,
mPendingMergedConfiguration
, mSurfaceControl
, mTempInsets
);
if (mSurfaceControl
.isValid()) {
mSurface
.copyFrom(mSurfaceControl
);
} else {
destroySurface();
}
...
}
我们来看下这段代码,等到 relayout 这个binder调用返回Surface之后,有了Surface之后呢,接下来的绘制就有了Buffer,在Buffer绘制完了之后,再提交到 SurfaceFlinger,SurfaceFlinger给图像合成好了就能写到屏幕中的缓冲区,然后我们页面就能显示出来了。
申请Surface 对页面显示是至关重要的一步
我们在回过头看刚刚的 mWindowSession.addToDisplay
2.11 WindowManagerGlobal::getWindowSession
public static IWindowSession
getWindowSession() {
synchronized (WindowManagerGlobal
.class) {
if (sWindowSession
== null
) {
try {
InputMethodManager
.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager
= getWindowManagerService();
sWindowSession
= windowManager
.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale
) {
ValueAnimator
.setDurationScale(scale
);
}
});
} catch (RemoteException e
) {
throw e
.rethrowFromSystemServer();
}
}
return sWindowSession
;
}
}
@UnsupportedAppUsage
public static IWindowManager
getWindowManagerService() {
synchronized (WindowManagerGlobal
.class) {
if (sWindowManagerService
== null
) {
sWindowManagerService
= IWindowManager
.Stub
.asInterface(
ServiceManager
.getService("window"));
try {
if (sWindowManagerService
!= null
) {
ValueAnimator
.setDurationScale(
sWindowManagerService
.getCurrentAnimatorScale());
}
} catch (RemoteException e
) {
throw e
.rethrowFromSystemServer();
}
}
return sWindowManagerService
;
}
}
我们可以看到这里实际上获取了 WMS的代理对象,调用了 openSession ,实际上有WMS来处理
2.12WindowManagerService:: openSession
- services/core/java/com/android/server/wm/WindowManagerService
@Override
public IWindowSession
openSession(IWindowSessionCallback callback
) {
return new Session(this, callback
);
}
这里用来给应用和WMS通信的,应用可以通过 IWindowSession对象向系统发起WMS调用
2.13 Session::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
);
}
这里的 IWindow是应用端提供给WMS调用应用端的相关接口,这样就保证了双向调用,这个 addWindow会创建一个Window相关的对象,然后WMS去管理所有的window的层级和位置还有大小。
WMS主要作用
分配surface掌管surface显示顺序及位置尺寸等控制窗口动画输入事件分发
总结
这个Activity之所以能显示出来,最重要的一步就是这个DecorView创建了一个ViewRootImpl对象,并且由 ViewRootImpl来全权负责,ViewRootImpl在 WMS注册了窗口,由WMS统一管理窗口的大小、位置还有层级,在第一次绘制的时候呢,ViewRootImpl还会向 WMS申请一个Surface,有了Surface之后呢,应用端就可以进行绘制了,绘制完之后呢,SurfaceFlinger就会按照WMS里面提供的window的层级位置对Surface进行绘制,然后在屏幕中的缓冲区显示。整个显示原理就这样了。
PhoneWindow是什么,怎么创建的setContentView原理,DecorView是什么ViewRootImpl是什么?有什么作用View的显示原理是什么?WMS发挥了什么作用
PhoneWindow的一个作用是给view包裹上一层DecorView。而DecorView中的布局结构,会根据requestWindowFeature()的不同而不同(requestWindowFeature()方法,会影响DecorView的孩子节点(layoutResource布局文件))。