一、前言
这篇文章主要是理解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 /*parent*/);
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);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
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() {
// cache mView since it is used so much below...
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
// Ask host how big it wants to be
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 {
// Emulate the legacy behavior. The global instance of InputMethodManager
// was instantiated here.
// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
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布局文件))。