Android深入浅出之Surface

    技术2025-12-22  35

    Android深入浅出之Surface[1] 收藏

    转自http://blog.csdn.net/Innost/archive/2011/02/05/6172893.aspx

    Android 深入浅出之Surface

    一 目的

    本节的目的就是为了讲清楚 Android 中的 Surface 系统,大家耳熟能详的 SurfaceFlinger 到底是个什么东西,它的工作流程又是怎样的。当然,鉴于 SurfaceFlinger 的复杂性,我们依然将采用情景分析的办法,找到合适的切入点。

    一个 Activity 是怎么在屏幕上显示出来的呢?我将首先把这个说清楚。

    接着我们把其中的关键调用抽象在 Native 层,以这些函数调用为切入点来研究 SurfaceFlinger 。好了,开始我们的征途吧。

    二 Activity 是如何显示的

    最初的想法就是, Activity 获得一块显存,然后在上面绘图,最后交给设备去显示。这个道理是没错,但是 Android 的 SurfaceFlinger 是在 System Server 进程中创建的, Activity 一般另有线程,这之间是如何 ... 如何挂上关系的呢?我可以先提前告诉大家,这个过程还比较复杂。呵呵。

    好吧,我们从 Activity 最初的启动开始。代码在

    framework/base/core/java/android/app/ActivityThread.java 中,这里有个函数叫 handleLaunchActivity

    [---->ActivityThread:: handleLaunchActivity()]

    private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {

          Activity a = performLaunchActivity(r, customIntent);

     

            if (a != null) {

                r.createdConfig = new Configuration(mConfiguration);

                Bundle oldState = r.state;

                handleResumeActivity(r.token, false, r.isForward);

    ----> 调用handleResumeActivity

    }

    handleLaunchActivity 中会调用 handleResumeActivity 。

    [--->ActivityThread:: handleResumeActivity]

    final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {

             boolean willBeVisible = !a.mStartedActivity;

              

    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;

                    if (a.mVisibleFromClient) {

                         a.mWindowAdded = true;

                        wm.addView(decor, l); // 这个很关键。

                    }

    上面 addView 那几行非常关键,它关系到咱们在 Activity 中 setContentView 后,整个 Window 到底都包含了些什么。我先告诉大家。所有你创建的 View 之上,还有一个 DecorView ,这是一个 FrameLayout ,另外还有一个 PhoneWindow 。上面这些东西的代码在

    framework/Policies/Base/Phone/com/android/Internal/policy/impl 。这些隐藏的 View 的创建都是由你在 Acitivty 的 onCreate 中调用 setContentView 导致的。

    [---->PhoneWindow:: addContentView]

       public void addContentView(View view, ViewGroup.LayoutParams params) {

            if (mContentParent == null) {  // 刚创建的时候mContentParent 为空

                installDecor();

            }

            mContentParent.addView(view, params);

            final Callback cb = getCallback();

            if (cb != null) {

                cb.onContentChanged();

            }

    }

    installDecor 将创建mDecor 和mContentParent 。mDecor 是DecorView 类型,

    mContentParent 是ViewGroup 类型

    private void installDecor() {

            if (mDecor == null) {

                mDecor = generateDecor();

                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

                mDecor.setIsRootNamespace(true);

            }

            if (mContentParent == null) {

                mContentParent = generateLayout(mDecor);

    那么, ViewManager wm = a.getWindowManager() 又返回什么呢?

    PhoneWindow 从 Window 中派生, Acitivity 创建的时候会调用它的 setWindowManager 。而这个函数由 Window 类实现。

    代码在 framework/base/core/java/android/view/Window.java 中

    public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {

            mAppToken = appToken;

            mAppName = appName;

            if (wm == null) {

                wm = WindowManagerImpl.getDefault();

            }

            mWindowManager = new LocalWindowManager(wm);

        }

    你看见没,分析 JAVA 代码这个东西真的很复杂。 mWindowManager 的实现是 LocalWindowManager ,但由通过 Bridge 模式把功能交给 WindowManagerImpl 去实现了。

    真的很复杂!

    好了,罗里罗嗦的,我们回到 wm.addView(decor, l) 。最终会由 WindowManagerImpl 来完成

    addView 操作,我们直接看它的实现好了。

    代码在 framework/base/core/java/android/view/WindowManagerImpl.java

    [---->addView]

    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)

        {

              ViewRoot root; //ViewRoot ,我们的主人公终于登场!

             synchronized (this) {

              root = new ViewRoot(view.getContext());

              root.mAddNesting = 1;

              view.setLayoutParams(wparams);

               

                if (mViews == null) {

                    index = 1;

                    mViews = new View[1];

                    mRoots = new ViewRoot[1];

                    mParams = new WindowManager.LayoutParams[1];

                } else {

               }

                index--;

                mViews[index] = view;

                mRoots[index] = root;

                mParams[index] = wparams;

            }

            root.setView(view, wparams, panelParentView);

    }

    ViewRoot 是整个显示系统中最为关键的东西,看起来这个东西好像和 View 有那么点关系,其实它根本和 View 等 UI 关系不大,它不过是一个 Handler 罢了,唯一有关系的就是它其中有一个变量为 Surface 类型。我们看看它的定义。 ViewRoot 代码在

    framework/base/core/java/android/view/ViewRoot.java 中

    public final class ViewRoot extends Handler implements ViewParent,

            View.AttachInfo.Callbacks

    {

    private final Surface mSurface = new Surface();

    }

    它竟然从handler 派生,而ViewParent 不过定义了一些接口函数罢了。

    看到 Surface 直觉上感到它和 SurfaceFlinger 有点关系。要不先去看看?

    Surface 代码在 framework/base/core/java/android/view/Surface.java 中,我们调用的是无参构造函数。

    public Surface() {

           mCanvas = new CompatibleCanvas(); // 就是创建一个Canvas !

    }

    如果你有兴趣的话,看看 Surface 其他构造函数,最终都会调用 native 的实现,而这些 native 的实现将和 SurfaceFlinger 建立关系,但我们这里 ViewRoot 中的 mSurface 显然还没有到这一步。那它到底是怎么和 SurfaceFlinger 搞上的呢?这一切待会就会水落石出的。

    另外,为什么 ViewRoot 是主人公呢?因为 ViewRoot 建立了客户端和 SystemServer 的关系。我们看看它的构造函数。

    public ViewRoot(Context context) {

            super();

           ....

           getWindowSession(context.getMainLooper());

    }

    getWindowsession 将建立和WindowManagerService 的关系。

    ublic static IWindowSession getWindowSession(Looper mainLooper) {

            synchronized (mStaticInit) {

                if (!mInitialized) {

                    try {

                    //sWindowSession 是通过Binder 机制创建的。终于让我们看到点希望了

                        InputMethodManager imm = InputMethodManager.getInstance(mainLooper);

                        sWindowSession = IWindowManager.Stub.asInterface(

                                ServiceManager.getService("window"))

                                .openSession(imm.getClient(), imm.getInputContext());

                         mInitialized = true;

                    } catch (RemoteException e) {

                    }

                }

                return sWindowSession;

            }

        }

    上面跨 Binder 的进程调用另一端是 WindowManagerService ,代码在

    framework/base/services/java/com/android/server/WindowManagerService.java 中。我们先不说这个。

    回过头来看看 ViewRoot 接下来的调用。

    [-->ViewRoot::setView()] ,这个函数很复杂,我们看其中关键几句。

    public void setView(View view, WindowManager.LayoutParams attrs,

                View panelParentView) {

            synchronized (this) {

                requestLayout();

                    try {

                        res = sWindowSession.add(mWindow, mWindowAttributes,

                                getHostVisibility(), mAttachInfo.mContentInsets);

                    }

    }

    requestLayout 实现很简单,就是往 handler 中发送了一个消息。

    public void requestLayout() {

            checkThread();

            mLayoutRequested = true;

            scheduleTraversals(); // 发送DO_TRAVERSAL 消息

    }

    public void scheduleTraversals() {

            if (!mTraversalScheduled) {

                 mTraversalScheduled = true;

                sendEmptyMessage(DO_TRAVERSAL);

            }

    }

    我们看看跨进程的那个调用。 sWindowSession.add 。它的最终实现在 WindowManagerService 中。

    [--->WindowSession::add()]

    public int add(IWindow window, WindowManager.LayoutParams attrs,

                     int viewVisibility, Rect outContentInsets) {

                return addWindow(this, window, attrs, viewVisibility, outContentInsets);

            }

    WindowSession 是个内部类,会调用外部类的 addWindow

    这个函数巨复杂无比,但是我们的核心目标是找到创建显示相关的部分。所以,最后精简的话就简单了。

    [--->WindowManagerService:: addWindow]

    public int addWindow(Session session, IWindow client,

                WindowManager.LayoutParams attrs, int viewVisibility,

                Rect outContentInsets) {

            // 创建一个WindowState ,这个又是什么玩意儿呢?

                  win = new WindowState(session, client, token,

                        attachedWindow, attrs, viewVisibility);

               win.attach();

               return res;

    }

    WindowState 类中有一个和 Surface 相关的成员变量,叫 SurfaceSession 。它会在

    attach 函数中被创建。 SurfaceSession 嘛,就和 SurfaceFlinger 有关系了。我们待会看。

    好,我们知道 ViewRoot 创建及调用 add 后,我们客户端的 View 系统就和 WindowManagerService 建立了牢不可破的关系。

    另外,我们知道 ViewRoot 是一个 handler ,而且刚才我们调用了 requestLayout ,所以接下来消息循环下一个将调用的就是 ViewRoot 的 handleMessage 。

    public void handleMessage(Message msg) {

            switch (msg.what) {

           case DO_TRAVERSAL:

                performTraversals();

    performTraversals 更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到 performTraversals ,不过我们现在更感兴趣的是 Surface 是如何创建的。

    private void performTraversals() {

            // cache mView since it is used so much below...

            final View host = mView;

     

              boolean initialized = false;

                boolean contentInsetsChanged = false;

                boolean visibleInsetsChanged;

                try {

    //ViewRoot 也有一个Surface 成员变量,叫mSurface ,这个就是代表SurfaceFlinger 的客户端

    //ViewRoot 在这个Surface 上作画,最后将由SurfaceFlinger 来合成显示。刚才说了mSurface 还没有什么内容。

               relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

    [---->ViewRoot:: relayoutWindow()]

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,

                boolean insetsPending) throws RemoteException {

           

    //relayOut 是跨进程调用,mSurface 做为参数传进去了,看来离真相越来越近了呀!

            int relayoutResult = sWindowSession.relayout(

                    mWindow, params,

                    (int) (mView.mMeasuredWidth * appScale + 0.5f),

                    (int) (mView.mMeasuredHeight * appScale + 0.5f),

                    viewVisibility, insetsPending, mWinFrame,

                    mPendingContentInsets, mPendingVisibleInsets,

                    mPendingConfiguration, mSurface); mSurface 做为参数传进去了。

           }

    我们赶紧转到 WindowManagerService 去看看吧。、

    public int relayoutWindow(Session session, IWindow client,

                WindowManager.LayoutParams attrs, int requestedWidth,

                int requestedHeight, int viewVisibility, boolean insetsPending,

                Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,

                 Configuration outConfig, Surface outSurface){

                   .....

              try {

               // 看到这里,我内心一阵狂喜,有戏,太有戏了!

             // 其中win 是我们最初创建的WindowState !

                        Surface surface = win.createSurfaceLocked();

                        if (surface != null) {

                      // 先创建一个本地surface ,然后把传入的参数outSurface copyFrom 一下

                             outSurface.copyFrom(surface);

                            win.mReportDestroySurface = false;

                            win.mSurfacePendingDestroy = false;

                           } else {

                           outSurface.release();

                         }

                    }

    }

    [--->WindowState::createSurfaceLocked]

    Surface createSurfaceLocked() {

             

                    try {

                        mSurface = new Surface(

                                mSession.mSurfaceSession, mSession.mPid,

                                 mAttrs.getTitle().toString(),

                                0, w, h, mAttrs.format, flags);

                      }

                    Surface.openTransaction();

    这里使用了 Surface 的另外一个构造函数。

        public Surface(SurfaceSession s,

                int pid, String name, int display, int w, int h, int format, int flags)

            throws OutOfResourcesException {

            mCanvas = new CompatibleCanvas();

            init(s,pid,name,display,w,h,format,flags); ----> 调用了native 的init 函数。

            mName = name;

    }

    到这里,不进入 JNI 是不可能说清楚了。不过我们要先回顾下之前的关键步骤。

    l          add 中, new 了一个 SurfaceSession

    l          创建 new 了一个 Surface

    l          调用 copyFrom ,把本地 Surface 信息传到 outSurface 中

    JNI 层

    上面两个类的 JNI 实现都在 framework/base/core/jni/android_view_Surface.cpp 中。

      [---->SurfaceSession:: SurfaceSession()]

    public class SurfaceSession {

        /** Create a new connection with the surface flinger. */

        public SurfaceSession() {

            init();

    }

    它的 init 函数对应为:

    [--->SurfaceSession_init]

    static void SurfaceSession_init(JNIEnv* env, jobject clazz)

    {

       //SurfaceSession 对应为SurfaceComposerClient

        sp<SurfaceComposerClient> client = new SurfaceComposerClient;

    client->incStrong(clazz);

    //Google 常用做法,在JAVA 对象中保存C++ 对象的指针。

        env->SetIntField(clazz, sso.client, (int)client.get());

    }

    Surface 的 init 对应为:

    [--->Surface_init]

    static void Surface_init(

            JNIEnv* env, jobject clazz,

            jobject session,

            jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)

    {

       SurfaceComposerClient* client =

                (SurfaceComposerClient*)env->GetIntField(session, sso.client);

     

        sp<SurfaceControl> surface;

    if (jname == NULL) {

       //client 是SurfaceComposerClient ,返回的surface 是一个SurfaceControl

      // 真得很复杂!

            surface = client->createSurface(pid, dpy, w, h, format, flags);

        } else {

            const jchar* str = env->GetStringCritical(jname, 0);

            const String8 name(str, env->GetStringLength(jname));

            env->ReleaseStringCritical(jname, str);

            surface = client->createSurface(pid, name, dpy, w, h, format, flags);

    }

      // 把surfaceControl 信息设置到Surface 对象中

        setSurfaceControl(env, clazz, surface);

    }

     

    static void setSurfaceControl(JNIEnv* env, jobject clazz,

            const sp<SurfaceControl>& surface)

    {

        SurfaceControl* const p =

            (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);

        if (surface.get()) {

            surface->incStrong(clazz);

        }

        if (p) {

            p->decStrong(clazz);

        }

        env->SetIntField(clazz, so.surfaceControl, (int)surface.get());

    }

    [--->Surface_copyFrom]

    static void Surface_copyFrom(

            JNIEnv* env, jobject clazz, jobject other)

    {

       const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);

        const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);

        if (!SurfaceControl::isSameSurface(surface, rhs)) {

            setSurfaceControl(env, clazz, rhs);

    // 把本地那个surface 的surfaceControl 对象转移到outSurface 上

        }

    }

    这里仅仅是 surfaceControl 的转移,但是并没有看到 Surface 相关的信息。

    那么 Surface 在哪里创建的呢?为了解释这个问题,我使用了终极武器, aidl 。

    1 终极武器 AIDL

    aidl 可以把 XXX.aidl 文件转换成对应的 java 文件。我们刚才调用的是 WindowSession 的

    relayOut 函数。如下:

    sWindowSession.relayout(

                    mWindow, params,

                    (int) (mView.mMeasuredWidth * appScale + 0.5f),

                    (int) (mView.mMeasuredHeight * appScale + 0.5f),

                    viewVisibility, insetsPending, mWinFrame,

                    mPendingContentInsets, mPendingVisibleInsets,

                    mPendingConfiguration, mSurface);

    它的 aidl 文件在 framework/base/core/java/android/view/IWindowSession.aidl 中

    interface IWindowSession {

        int add(IWindow window, in WindowManager.LayoutParams attrs,

                in int viewVisibility, out Rect outContentInsets);

        void remove(IWindow window);

       // 注意喔,这个outSurface 前面的是out ,表示输出参数,这个类似于C++ 的引用。

      int relayout(IWindow window, in WindowManager.LayoutParams attrs,

                int requestedWidth, int requestedHeight, int viewVisibility,

                boolean insetsPending, out Rect outFrame, out Rect outContentInsets,

                out Rect outVisibleInsets, out Configuration outConfig,

                out Surface outSurface);

    刚才说了, JNI 及其 JAVA 调用只是 copyFrom 了 SurfaceControl 对象到 outSurface 中,但是没看到哪里创建 Surface 。这其中的奥秘就在 aidl 文件编译后生成的 java 文件中。

    你在命令行下可以输入:

    aidl -Id:/android-2.2-froyo-20100625-source/source/frameworks/base/core/java/ -Id:/android-2.2-froyo-20100625-source/source/frameworks/base/Graphics/java d:/android-2.2-froyo-20100625-source/source/frameworks/base/core/java/android/view/IWindowSession.aidl test.java

    以生成 test.java 文件。 -I 参数指定 include 目录,例如 aidl 有些参数是在别的 java 文件中指定的,那么这个 -I 就需要把这些目录包含进来。

    先看看 ViewRoot 这个客户端生成的代码是什么。

    public int relayout(

    android.view.IWindow window,

    android.view.WindowManager.LayoutParams attrs,

      int requestedWidth, int requestedHeight,

    int viewVisibility, boolean insetsPending,

    android.graphics.Rect outFrame,

    android.graphics.Rect outContentInsets,

        android.graphics.Rect outVisibleInsets,

    android.content.res.Configuration outConfig,

    android.view.Surface outSurface) ---->outSurface 是第11 个参数

    throws android.os.RemoteException

    {

    android.os.Parcel _data = android.os.Parcel.obtain();

    android.os.Parcel _reply = android.os.Parcel.obtain();

    int _result;

    try {

    _data.writeInterfaceToken(DESCRIPTOR);

    _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));

    if ((attrs!=null)) {

    _data.writeInt(1);

    attrs.writeToParcel(_data, 0);

    }

    else {

    _data.writeInt(0);

    }

    _data.writeInt(requestedWidth);

    _data.writeInt(requestedHeight);

    _data.writeInt(viewVisibility);

    _data.writeInt(((insetsPending)?(1):(0)));

      // 奇怪,outSurface 的信息没有写到_data 中。那.....

    mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);

    _reply.readException();

    _result = _reply.readInt();

    if ((0!=_reply.readInt())) {

    outFrame.readFromParcel(_reply);

    }

    ....

    if ((0!=_reply.readInt())) {

    outSurface.readFromParcel(_reply); // 从Parcel 中读取信息来填充outSurface

    }

    }

    finally {

    _reply.recycle();

    _data.recycle();

    }

    return _result;

    }

    真奇怪啊, Binder 客户端这头竟然没有把 outSurface 的信息发过去。我们赶紧看看服务端。

    服务端这边处理是在 onTranscat 函数中。

    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

    {

    switch (code)

    {

    case TRANSACTION_relayout:

    {

    data.enforceInterface(DESCRIPTOR);

    android.view.IWindow _arg0;

    android.view.Surface _arg10;

    // 刚才说了,Surface 信息并没有传过来,那么我们在relayOut 中看到的outSurface 是怎么

    // 出来的呢?看下面这句,原来在服务端这边竟然new 了一个新的Surface !!!

    _arg10 = new android.view.Surface();

    int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);

    reply.writeNoException();

    reply.writeInt(_result);

    //_arg10 是copyFrom 了,那怎么传到客户端呢?

    if ((_arg10!=null)) {

    reply.writeInt(1);// 调用Surface 的writeToParcel ,把信息加入reply

    _arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

    }

    return true;

    }

    太诡异了!竟然有这么多花花肠子。我相信如果没有 aidl 的帮助,我无论如何也不会知道这其中的奥妙。

    那好,我们的流程明白了。

    l          客户端虽然传了一个 surface ,但其实没传递给服务端

    l          服务端调用 writeToParcel ,把信息写到 Parcel 中,然后数据传回客户端

    l          客户端调用 Surface 的 readFromParcel ,获得 surface 信息。

    那就去看看 writeToParcel 吧。

    [---->Surface_writeToParcel]

    static void Surface_writeToParcel(

            JNIEnv* env, jobject clazz, jobject argParcel, jint flags)

    {

        Parcel* parcel = (Parcel*)env->GetIntField(

                argParcel, no.native_parcel);

      

    const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));

    // 还好,只是把数据序列化到Parcel 中

        SurfaceControl::writeSurfaceToParcel(control, parcel);

        if (flags & PARCELABLE_WRITE_RETURN_VALUE) {

            setSurfaceControl(env, clazz, 0);

        }

    }

    那看看客户端的 Surface_readFromParcel 吧。

    [----->Surface_readFromParcel]

    static void Surface_readFromParcel(

            JNIEnv* env, jobject clazz, jobject argParcel)

    {

        Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);

      

       // 客户端这边还没有surface 呢

      const sp<Surface>& control(getSurface(env, clazz));

      // 不过我们看到希望了,根据服务端那边Parcel 信息来构造一个新的surface

        sp<Surface> rhs = new Surface(*parcel);

        if (!Surface::isSameSurface(control, rhs)) {

           setSurface(env, clazz, rhs); // 把这个新surface 赋给客户端。终于我们有了surface !

        }

    }

    到此,我们终于七拐八绕的得到了 surface ,这其中经历太多曲折了。下一节,我们将精简这其中复杂的操作,统一归到 Native 层,以这样为切入点来了解 Surface 的工作流程和原理。

    好,反正你知道 ViewRoot 调用了 relayout 后, Surface 就真正从 WindowManagerService 那得到了。继续回到 ViewRoot ,其中还有一个重要地方是我们知道却不了解的。

    private void performTraversals() {

            // cache mView since it is used so much below...

            final View host = mView;

     

             boolean initialized = false;

                boolean contentInsetsChanged = false;

                boolean visibleInsetsChanged;

                try {

             relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

          // relayoutWindow 完后,我们得到了一个无比宝贵的Surface

         // 那我们画界面的地方在哪里?就在这个函数中,离relayoutWindow 不远处。

    ....

      boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();

     

            if (!cancelDraw && !newSurface) {

                mFullRedrawNeeded = false;

                draw(fullRedrawNeeded); //draw?draw 什么呀?

           }

        [--->ViewRoot::draw()]

    private void draw(boolean fullRedrawNeeded) {

            Surface surface = mSurface; // 嘿嘿,不担心了,surface 资源都齐全了

            if (surface == null || !surface.isValid()) {

                return;

            }

            if (mAttachInfo.mViewScrollChanged) {

                mAttachInfo.mViewScrollChanged = false;

                mAttachInfo.mTreeObserver.dispatchOnScrollChanged();

            }

     

            int yoff;

            final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();

            if (scrolling) {

                yoff = mScroller.getCurrY();

            } else {

                yoff = mScrollY;

            }

            if (mCurScrollY != yoff) {

                mCurScrollY = yoff;

                fullRedrawNeeded = true;

            }

            float appScale = mAttachInfo.mApplicationScale;

            boolean scalingRequired = mAttachInfo.mScalingRequired;

     

            Rect dirty = mDirty;

            if (mUseGL) { // 我们不用OPENGL

               ...

            }

     

             Canvas canvas;

            try {

                int left = dirty.left;

                int top = dirty.top;

                int right = dirty.right;

                int bottom = dirty.bottom;

              // 从Surface 中锁定一块区域,这块区域是我们认为的需要重绘的区域

                canvas = surface.lockCanvas(dirty);

                // TODO: Do this in native

                canvas.setDensity(mDensity);

            }

     

            try {

                 if (!dirty.isEmpty() || mIsAnimating) {

                    long startTime = 0L;

                    try {

                        canvas.translate(0, -yoff);

                        if (mTranslator != null) {

                            mTranslator.translateCanvas(canvas);

                        }

                        canvas.setScreenDensity(scalingRequired

                                ? DisplayMetrics.DENSITY_DEVICE : 0);

                     //mView 就是之前的decoreView,

                        mView.draw(canvas);

                    }

                } finally {

                 // 我们的图画完了,告诉surface 释放这块区域

                surface.unlockCanvasAndPost(canvas);

            }

           if (scrolling) {

                 mFullRedrawNeeded = true;

                scheduleTraversals();

            }

    }

    看起来,这个 surface 的用法很简单嘛:

    l          lockSurface ,得到一个画布 Canvas

    l          调用 View 的 draw ,让他们在这个 Canvas 上尽情绘图才。另外,这个 View 会调用所有它的子 View 来画图,最终会进入到 View 的 onDraw 函数中,在这里我们可以做定制化的界面美化工作。当然,如果你想定制化整个系统画图的话,完全可以把 performTranvsal 看懂,然后再修改。

    l          unlockCanvasAndPost ,告诉 Surface 释放这块画布

    当然,这几个重要函数调用干了具体的活。这些重要函数,我们最终会精简到 Native 层的。

    2 总结

    到这里,你应该知道了一个 Activity 中,调用 setContentView 后它如何从系统中获取一块 Surface ,以及它是如何使用这个 Surface 的了。不得不说,关于 UI 这块, Android 绝对是够复杂的。难怪 2.3 把 UI 这块代码基本重写一遍,希望能够简单精炼点。

    最新回复(0)