Android系统Activity的显示原理

 本文查看的源码是Android 7.1

所有方法中的代码只保留了本文关注的代码。

抽空研究了一下Activity的显示过程,主要是熟悉Android中的Activiy从设置布局到绘制显示的整个流程。

按照老惯例先上时序图

涉及的文件路径:

frameworks/base/core/java/android/app/Activity.java
frameworks/base/core/java/android/view/ViewRootImpl.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/view/WindowManagerGlobal.java
frameworks/base/services/core/java/com/android/server/wm/Session.java
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

一、建立ViewTree数据结构

我们查看的入口函数是Activity 的oncreate方法中使用的setContentView,我们开发过程中必须要设置布局文件或者布局对象,因此以此为入口比较合适。

frameworks/base/core/java/android/app/Activity.java

    public void setContentView(@LayoutRes int layoutResID) {
        //拿到Window对象,调用Window的setContentView方法
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

setContentView 调用的是PhoneWindow的setContentView,使用PhoneWindow 首先要先拿到PhoneWindow ,而PhoneWindow是在Activity进程创建之后由ActivityThread调用attach创建的。

Activity的创建大体分为如下步骤,这里细节不在赘述。
1.创建Activity对象。
2.创建COntext对象。
3.准备APPlication对象。
4.attach上下文。
5.Activity onCreate。

    final void attach(Context context, ......) {
        //Window 创建PhoneWindow对象
        //attach 方法是在Activity启动的时候创建的
        //Activity 创建的步骤
        //1.创建Activity对象
        //2.创建COntext对象
        //3.准备APPlication对象
        //4.attach上下文
        //5.Activity onCreate        
        mWindow = new PhoneWindow(this, window);
    }

继续查看PhoneWindow 中的setContentView 方法。

这个方法中:

调用installDecor 创建了DecorView对象。

调用mLayoutInflater.inflate(layoutResID, mContentParent); 方法将生成的ViewTree添加到mContentParent对象中。

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

    @Override
    public void setContentView(int layoutResID) {

        if (mContentParent == null) {
            //创建DecorView
            installDecor();
        }
        // 加载布局用的
        //layoutResID 加载布局的id 会生成ViewTree
        // mContentParent 是ViewTree的Patent 把生成的layoutResID 添加到 mContentParent 中
        mLayoutInflater.inflate(layoutResID, mContentParent);

    }

我们继续查看installDecor 方法。

这个方法中除了创建DecorView 还设置了Activity的主题以及mContentParent对象。

到这里只是建立了ViewTree数据结构,并没有执行绘制动作。

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //创建 mDecor 对象 是一个FrameLayout 是手机页面的整个RootView
            //generateDecor 设置页面的主题
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            //获取 mContentParent 对象
            mContentParent = generateLayout(mDecor);
        }
    }

二、真正的绘制流程

我们都知道真正的绘制流程是在ActivityThread的handleResumeActivity方法发起的,也就是在调用Activity的onResume之后。

我们查看ActivityThread的handleResumeActivity方法,在handleResumeActivity 方法中大体做了如下绘制相关的事情:

调用Activity的onResume方法。

获取ViewManager 将 DecorView添加到ViewManager中。

使Activity变成可见。

frameworks/base/core/java/android/app/ActivityThread.java

    final void handleResumeActivity(IBinder token,.........) {
        ActivityClientRecord r = mActivities.get(token);
        //触发了 Activity的onResume 方法
        //触发之后才会处理UI的显示问题
        r = performResumeActivity(token, clearHide, reason);
        final Activity a = r.activity;
        r.window = r.activity.getWindow();
        //先拿到Activity 的DecorView
        View decor = r.window.getDecorView();
        ViewManager wm = a.getWindowManager();
        a.mDecor = decor;
        if (a.mVisibleFromClient && !a.mWindowAdded) {
            //将Activity 的DecorView 添加到ViewManager里面
            wm.addView(decor, l);
        }
        if (r.activity.mVisibleFromClient) {
            //使界面变成可见界面
            r.activity.makeVisible();
        }
    }

ViewManager是一个接口类,具体的实现在WindowManagerImpl 中,我们查看到WindowManagerImpl的addView方法又调用了WindowManagerGlobal的addView方法。

继续往下查找。

frameworks/base/core/java/android/view/WindowManagerImpl.java

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        android.util.SeempLog.record_vg_layout(383,params);
        applyDefaultToken(params);
        //调用到 WindowManagerGlobal 中的 addView 方法
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

在WindowManagerGlobal中我们看到创建了ViewRootImpl对象,并且将DecorView交付给ViewRootImpl进行管理。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ViewRootImpl root;
        synchronized (mLock) {
            //初始化 ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
        }
        try {
            //将DecorView 交给 ViewRootImpl 进行管理
            root.setView(view, wparams, panelParentView);
        }
    }

继续查看ViewRootImpl的setView方法,发现了请求绘制的方法 requestLayout();。

在请求绘制之后又调用了 mWindowSession.addToDisplay 让WMS进行显示。在这里我们分开查看  requestLayout 和 mWindowSession.addToDisplay 分别做了那些事情?

frameworks/base/core/java/android/view/ViewRootImpl.java

    public void setView(View view,.......) {
        synchronized (this) {
            //一个ViewRootImp 只能管理一个ViewTree
            if (mView == null) {
                mView = view;
                //请求绘制
                requestLayout();
                // Binder 调用 调用到 IWindowSession.aidl
                // Session.java 实现了 IWindowSession.Stub 也就是
                // Session 实现了 addToDisplay 方法 跨进程到了 WindowManagerService中
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
        }
    }

2.1首先查看requestLayout

requestLayout方法调用了scheduleTraversals 方法,继续往下看。

frameworks/base/core/java/android/view/ViewRootImpl.java

    @Override
    public void requestLayout() {
            scheduleTraversals();
    }

查看到scheduleTraversals方法在Choreographer 里面 post 一个mTraversalRunnable对象而mTraversalRunnable对象会在下一次Vsyn 信号来的时候执行mTraversalRunnable 方法。

Vsyn什么时候来?怎么来?我们在这篇文档里面不去深究。

我们只需要关心mTraversalRunnable 干了什么事情。继续查看mTraversalRunnable。

void scheduleTraversals() {
        if (!mTraversalScheduled) {
             // 往Choreographer 里面 post 一个mTraversalRunnable
             // mTraversalRunnable 会在下一次Vsyn 信号来的时候执行绘制操作
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

 执行了 doTraversal();方法,继续往下查看。

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

调用到了  performTraversals()方法  performTraversals()方法才是真正绘制的地方。 

继续查看 performTraversals()方法。

   void doTraversal() {
        if (mTraversalScheduled) {
            // 真正执行绘制的地方
            performTraversals();
        }
    }

 performTraversals()方法中我们只关心几个重要方法和重要事情。

向WMS申请Surface,如果Surface申请不成功,就不能进行绘制操作。

1.测量ViewTree中的所有控件。 

2.摆放控件。

3.画出控件。
关于 performMeasure,performLayout ,performDraw();这三个方法我们在这里不再赘述。

    private void performTraversals() {
            // 向WMS申请Surface
            //非常关键
            relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
           //测量 
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            //摆放
            performLayout(lp, mWidth, mHeight);
            //绘制
            performDraw();
    }

我们查看relayoutWindow方法中调用的 WindowSession.relayout 调用的是WMS中的relayout

方法,而这个方法主要做的事情是将申请的Surface可用化,如果未申请之前Surface是个空壳,是不可用的状态,无法进行显示。

申请完成Surface之后,就可以调用performDraw将结果绘制到Buffer中,然后提交给SurfaceFiling进行合成图片,再由SurfaceFiling提交到屏幕显示缓冲区,等待VSYN信号显示到屏幕上。

    private int relayoutWindow(WindowManager.LayoutParams params, .......)  {
             //申请Surface
            // 申请Surface 通过Binder调用之后 Surface才能使用,
            // 未调用之前Surface是个空壳
            // 1.有了surface之后就有了Buffer
            // 2.在Buffer中绘制完成之后开始提交给SurfaceFling
            // 3.SurfaceFling合成图片 提交到屏幕的缓冲区 
            // 4.经过VSYN周期刷新到屏幕上面显示
        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,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
                mSurface);
    }

2.2查看mWindowSession.addToDisplay

首先我们先看看 mWindowSession 是在什么地方获取的?

mWindowSession是在WindowManagerGlobal类getWindowSession方法获取的。

getWindowSession方法调用了windowManager.openSession是属于Binder调用。

继续查看windowManager.openSession方法。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

    public static IWindowSession getWindowSession() {
        if (sWindowSession == null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            IWindowManager windowManager = getWindowManagerService();
            sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {},....);
        }
        return sWindowSession;
    }

openSession方法在WindowManagerService中,查看到此方法创建了Session 而 Session 继承于IWindowSession,IWindowSession是个aidl文件用来跨进程通讯的,有此知道可以将WindowManagerGlobal 和 WindowManagerService通过 Binder跨进程通讯。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    @Override
    public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
            IInputContext inputContext) {
        if (client == null) throw new IllegalArgumentException("null client");
        if (inputContext == null) throw new IllegalArgumentException("null inputContext");
        Session session = new Session(this, callback, client, inputContext);
        return session;
    }

frameworks/base/services/core/java/com/android/server/wm/Session.java

final class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    ......
}

因为Session继承了 IWindowSession.Stub 因此调用 mWindowSession.addToDisplay 其实调用的是 Session.java 的 addToDisplay 方法。

其中 mService.addWindow调用WindowManagerService的 addwindows 方法

frameworks/base/services/core/java/com/android/server/wm/Session.java

  @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        //调用WMS的 addwindows 方法
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

我们查看到WindowManagerService 中的addWindow 方法有两个参数值得注意 ,这两个参数可以让WMS和WIndow相互之间调用。

 IWindow client binder参数对象 WMS与Windows进行通讯
Session session binder参数对象 Window与WMS进行通讯

WindowManagerService的addWindow方法中我们大体能看到WindowManagerService的方法主要包括下面四个方面:

1.分配Serface。
2.掌管Surface显示顺序以及位置尺寸等。
3.控制窗口动画。
4.输入事件分发。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    //IWindow client binder对象 WMS与Windows进行通讯
    //Session session binder对象 Window与WMS进行通讯
    public int addWindow(Session session, IWindow client, int seq,.......) {
                       
                        |  1.分配Serface
        WMS的主要作用 --->|  2.掌管Surface显示顺序以及位置尺寸等
                        |  3.控制窗口动画      
                        |  4.输入事件分发     
    }

最后我们总结一下:


Activity创建的时候会创建一个PhoneWindow,PhoneWindow会有一个DecorView,DecorView就是这个Activity的整个View的ViewTree。ContentView只是DecorView的一部分,DecorView会对应一个ViewRootImpl对象,ViewRootImpl可以和WindowManagerService双向通信,ViewRootImpl通过IWindowSession向WindowManagerService发起Binder调用,WindowManagerService可以通过IWindow向ViewRootImpl发起Binder调用。Activity之所以能显示是因为DecorView创建了ViewRootImpl对象,并且ViewRootImpl全权负责DecorView的绘制,ViewRootImpl会在WindowManagerService注册一个窗口,然后WindowManagerService会统一管理子View的窗口大小,位置,还有层级。在第一次绘制的时候ViewRootImpl会向WindowManagerService申请一个Surface,有了Surface之后应用端就可以进行绘制了,绘制完了之后SurfaceFling就会按照WindowManagerService窗口大小,位置,还有层级进行合成,合成之后就会写到屏幕的显示缓冲区显示出来。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值