Activity的显示原理分析

概要

在Android开发中,通常我们作了startActivity之后,一会就加载出来了Activity页面,那么,这个Activity是到底是如何显示出来的呢?Activity的显示主要包括以下几个方面。

  • Activity的显示原理(Window/DecorView,ViewRoot)

  • Activity的UI刷新机制(Vsync/Choreographer)

  • UI的绘制原理(Measure/Layout/onDraw)

  • Surface原理(Surface/SurfaceFlinger)

Activity的显示原理

Activity的显示原理主要从以下几个方面进行分析:

1、Activity的setContentView()原理分析

首先来看看Activity的setConentView长什么样的

public void setContentView(int layoutResID) {
        //attach中初始化window
        getWindow().setContentView(layoutResID);
        initActionBar();
    }

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) {
...
mWindow = PolicyManager.makeNewWindow(this)
}

public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

从上面看起来还是比较简单的,这里不详细分析,简单说明一下,getWindow()实际上get出来的PhoneWindow。继续关注PhoneWindow中的setContentView()方法是什么样的。

@Override
    public void setContentView(int layoutResID) {
        //这里mContentParent就是我们setContentView进来的view的父容器,DecorView是整个手机屏幕的父容器
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

private void installDecor() {
        if (mDecor == null) {
            //这里就是new DecorView(getContext(), -1);
            mDecor = generateDecor();
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            } else {
                //处理ActionBar的,这里我们不关心
            }
    }

protected ViewGroup generateLayout(DecorView decor) {
        ...
        //layoutResource即为整个屏幕的布局文件,包括顶的通知栏,statusBar,navgationBar以及中间的contentView的parent
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ...
        return contentParent;
    }

以上步骤还只是把我们的contentView加载到decorView中,还没有显示。要Activity的onResume之后才会显示,下面看看onResume之后显示的原因。

2、Activity在onResume()之后显示原因分析

前面我们分析Activity的启动生命周期时有讲到加载Activity时,在调用ActivityThread中的handleLauncheActivity之后会走handleResumeActivity中,从而回调Activity的onResume()方法,这里我们再来看看handleResumeActivity的方法是什么样的

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
                                    boolean reallyResume) {
        //触发Activity的onResume()回调
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        final Activity a = r.activity;

        if (r.window == null && !a.mFinished) {
            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 (a.mVisibleFromClient) {
                a.mWindowAdded = true;

                //拿到Activity的PhoneWindow的WindowManager,
                // 通过WindowManager的addView将Activity的ContentView添加,从而显示
                wm.addView(decor, l);
            }
        }
        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
    }

以上的步骤我们看到最核心的操作就是把contentView通过WindowManager的方法addView进去,那么add进去是之后是如何对该View的显示啊,布局,绘制,以及事件处理的呢,重点就在addView里面。进一步看看WindowManager的addView方法,实际上是在WindowManager作为一个接口,在它的实现类WindowManagerImpl中

@Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        //WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
        //WindowManagerGlobal是全局管理window
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

public void addView(View view, ViewGroup.LayoutParams params,
                        Display display, Window parentWindow) {
        ...
        ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
        //ViewRootImpl管理View,一个ViewRoolImpl只能管理一个ViewTree
        root.setView(view, wparams, panelParentView);
    }

这里我们需要进一步分析ViewRootImpl中setView是如何处理的,代码块如下

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ...

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //触发第一次绘制
                requestLayout();
                ...
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mInputChannel);
            }
        }
    }

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            //核心方法
            scheduleTraversals();
        }
    }

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            //核心方法,该Callback会在下一次Vsync信号来的时候调用
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

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

void doTraversal() {
        if (mTraversalScheduled) {
            //这里面执行measure, layout, draw
            performTraversals();
        }
    }

private void performTraversals() {
        ...
        //下面四大核心方法,这里直观的描述一下这个显示过程:
        //在这个方法之前,Surface实际上还是一个空的,处于不可用状态
        //用来向WMS申请Surface,实际上就是通过binder对象向WMS请求一个可以用的Surface
        //有了可用的surface之后,绘制就会有buffer,在buffer上绘制完成之后,就可以把这个buffer提交到SurfaceFlinger,
        //SurfaceFlinger将图像合成好之后,下一步即可写到屏幕的中缓存区,从而页面显示出来。
        relayoutWindow(params, viewVisibility, insetsPending);
        ...
        //对应onMeasure()
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
        //对应onLayout()
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        ...
        //对应onDraw()
        performDraw();
    }

从上面我们已经跟到ViewRootImpl中的performTraversals()函数,在这个函数中我们发现了平常我们常用的measure,layout,draw的回调地方。这上面步骤中作完后,界面好像就显示出来了,那么这个到底是怎么显示出来的呢?

答案是上面的mWindowSession.addToDisplay()方法。这里mWindowSession实际是一个WMS的binder对象,实际上应用会和WMS双向绑定,相互调用。addToDisplay()最终调用到的是Session中的方法

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {
        //通过wms对象添加window,并对window进行管理
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);
    }

这里总结一下WMS的主要作用,主要分为以下4大作用

1、分配Surface

2、控制Surface的显示顺序以及位置尺寸等

3、控制窗口动画

4、输入事件分发

发布了11 篇原创文章 · 获赞 3 · 访问量 773

猜你喜欢

转载自blog.csdn.net/xj_hnust/article/details/98099978
今日推荐