深入理解Activity(二) ----- Activity显示原理

    科技2022-07-12  145

    一、前言

    这篇文章主要是理解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布局文件))。

    Processed: 0.016, SQL: 8