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

一、前言

这篇文章主要是理解Acitivty对应的视图是如何显示出来的, 每次使用对应的Activity 我们都会去使用 setContentView,今天我们将看下AndroidView的显示原理。

  • 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显示出来的呢,主要是调用了上面这个函数。
这里通过 ViewManagerDecorView加入,再通过 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(); 
        }

relayoutWindowWMS申请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布局文件))。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值