浅析 android 应用界面的展现流程(四)创建绘制表面

在《浅析 android 应用界面的展现流程(二)布局与视图的创建》最后我们提到,WindowManagerImpl.setView() 方法第二步是 requestLayout(),本文针对该函数进行展开分析,是开始布局与绘制的过程。

5. 创建绘制表面

回顾一下 ViewRootImpl.setView() 的源码:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            ......
            // 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.
2.          requestLayout(); // 首次调度执行 layout,这里会触发 onAttachToWindow 和 创建 Surface
            ......
        }
    }
}

5.0 requestLayout()

public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

可见实际就是调用 scheduleTraversals() 了,在 Android 4.0 及以前,该函数只是由主线程 Handler 驱动的调用,像这样(Android 4.0 源码):

public void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        ......
        sendEmptyMessage(DO_TRAVERSAL);
    }
}

从 Android 4.1 开始,引入 Project Butter,Project Butter对Android Display系统进行了重构,引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer,有效解决了一直以来 Android 系统 UI 的卡顿,大家可以参考这篇文章(邓凡平写的),在这里我们不做分析了。

真正的布局是在 ViewRootImpl 的 performTraversals() 函数中完成的,如图 5.1 所示为从 WindowManagerGlobal 的 addView 开始到 performTraversals 的流程:
<center>

addView.jpg

图 5.1 performTraversals 调用流程</center>
中间的一些调用不是很重要,在这里就不再累述了,我们直接来看 ViewRootImpl 的 performTraversals() 方法,在过这一段流程前,我们先要注意下几个字段:

  1. mLayoutRequested - boolean - 表示是否正在 requestLayout
  2. mFirst - boolean - 是不是第一次 requestLayout
  3. mFullRedrawNeeded - boolean - 是否要将窗口全部区域重绘,当 mFirst 为 true 时,mFullRedrawNeeded 肯定为 true

该方法在 Android4.4 源码中长达 771 行,在这里我们只关注与 Activity 的回调函数相关的代码:

private void performTraversals() {
    final View host = mView; // 缓存为局部变量 host
    ......
1.  mIsInTraversal = true; // 表明现在正在performTraversals函数执行中
    ......
2.  if (mFirst) { // 如果是首次 requestLayout 则 mFullRedrawNeeded = mLayoutRequested = true,并执行 dispatchAttachedToWindow,此处是真正的 onAttachToWindow
        mFullRedrawNeeded = true;
        mLayoutRequested = true;
        if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
                || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
            ......
        } else {
            DisplayMetrics packageMetrics =
                mView.getContext().getResources().getDisplayMetrics();
            desiredWindowWidth = packageMetrics.widthPixels;
            desiredWindowHeight = packageMetrics.heightPixels;
        }
        ......
        host.dispatchAttachedToWindow(attachInfo, 0);
        ......
    }
    ......
    boolean layoutRequested = mLayoutRequested && !mStopped;
    if (layoutRequested) {
        ......
        // Ask host how big it wants to be
3.      windowSizeMayChange |= measureHierarchy(host, lp, res,
                desiredWindowWidth, desiredWindowHeight); // let's measure and onMeasure!
    }

    ......

    if (layoutRequested) {
4.      mLayoutRequested = false; // 重置为 false,表示本次requestLayout得到了处理
    }

    boolean windowShouldResize = layoutRequested && windowSizeMayChange
        && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
            || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                    frame.width() < desiredWindowWidth && frame.width() != mWidth)
            || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                    frame.height() < desiredWindowHeight && frame.height() != mHeight)); // 窗体是否需要重新布局

    ......

    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null) {

        ......

        boolean hadSurface = mSurface.isValid();

        try {
            ......
5.          relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); // 
           
            ......

            if (!hadSurface) {
                if (mSurface.isValid()) { // 如果本次是新建的 surface,则整个窗体需要重绘
6.                  newSurface = true;
                    mFullRedrawNeeded = true;
                    ......
                }
            } else if (...) {
                ......
            } else if (...) {
                ......
            }
        } catch (RemoteException e) {
        }
        ......
    } else {
        ......
    }

    final boolean didLayout = layoutRequested && !mStopped;
    ......
    if (didLayout) {
7.      performLayout(lp, desiredWindowWidth, desiredWindowHeight); // 真正的 layout 和 onLayout
        .......
    }
    ......
    boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
            viewVisibility != View.VISIBLE;

    if (!cancelDraw && !newSurface) {
        if (!skipDraw || mReportNextDraw) {
            ......
8.          performDraw(); // 真正的 draw 和 onDraw
        }
    } else {
        if (viewVisibility == View.VISIBLE) {
            // Try again
            scheduleTraversals();
        } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
                mPendingTransitions.get(i).endChangingAnimations();
            }
            mPendingTransitions.clear();
        }
    }

    mIsInTraversal = false;
}

步骤1:在一进入 performTraversals 函数,就将 mIsInTraversal 置为 true,表示现在正在performTraversals函数执行中(此变量在 remmoveView 时有用);
步骤2:首次执行 requestLayout 时 mLayoutRequested 会被置为 true,同时执行 dispatchAttachedToWindow,此时各层 View 以及 Activity 的 onAttachToWindow 将会被执行;
步骤3:对窗体进行 measure;
步骤4:重置 mLayoutRequested;
步骤5:relayoutWindow 创建 surface,WMS 如果发现当前正在处理的窗口尚未具有一个有效的 surface,那么就会为它创建一个有效的 surface;
步骤6:在 relayoutWindow 之前执行了一次 boolean hadSurface = mSurface.isValid(); 即在创建 surface 之前的状态,在执行为 relayoutWindow 后再次判断 mSurface.isValid(),如果前后不一样,那么本次肯定是首次创建 surface,置 newSurface = mFullRedrawNeeded = true,mFullRedrawNeeded 表示在 performDraw 中告诉各 View 要全部重新绘制;
**步骤7:performLayout 进行窗体布局;
步骤8:performDraw 进行绘制**
我们要重点关注的就是上面加粗的5个步骤,本文中重点对创建绘制表面的过程进行展开,measure、performLayout、perfromDraw 的详细流程将会在后续的几篇文章中继续展开分析。

5.1 首次执行 requestLayout 时执行 dispatchAttachedToWindow

对应步骤2,首次执行 requestLayout 时即在 Activity 首次执行 onResume 后,ViewGroup.dispatchAttachedToWindow(因为 DecorView 是 FrameLayout 类型)会被执行:

@Override
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
    super.dispatchAttachedToWindow(info, visibility); // View.dispatchAttachedToWindow
    mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

    final int count = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < count; i++) {
        final View child = children[i];
        child.dispatchAttachedToWindow(info,
                visibility | (child.mViewFlags & VISIBILITY_MASK)); // 子 view 的 dispatchAttachedToWindow
    }
}

首先是执行父类的 dispatchAttachedToWindow 方法,然后是对每个子 View 都执行 dispatchAttachedToWindow() 方法。在这里来看父类的 dispatchAttachedToWindow 方法,也就是 View.dispatchAttachedToWindow():

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    //System.out.println("Attached! " + this);
    mAttachInfo = info;
    if (mOverlay != null) {
1.      mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
    }
    ......
2.  onAttachedToWindow();

    ListenerInfo li = mListenerInfo;
    final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
            li != null ? li.mOnAttachStateChangeListeners : null;
    if (listeners != null && listeners.size() > 0) {
        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
        // perform the dispatching. The iterator is a safe guard against listeners that
        // could mutate the list by calling the various add/remove methods. This prevents
        // the array from being modified while we iterate it.
        for (OnAttachStateChangeListener listener : listeners) {
3.          listener.onViewAttachedToWindow(this);
        }
    }

    int vis = info.mWindowVisibility;
    if (vis != GONE) {
4.      onWindowVisibilityChanged(vis);
    }
    if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {
        // If nobody has evaluated the drawable state yet, then do it now.
        refreshDrawableState();
    }
    needGlobalAttributesUpdate(false);
}

1:仅 >4.3 源码才有的东西,ViewOverlay 是最上面的一个透明的层,我们可以在这个层之上添加内容而不会影响到整个布局结构,也不会响应点击事件,4.4 上 transaction 动画就是基于此做的;

2:onAttachedToWindow() 回调,onAttachedToWindow 真正执行顺序依次是 View.onAttachedToWindow、ViewGroup.onAttachedToWindow、DecorView.onAttachedToWindow(当然表面的调用顺序是反过来的,使用 super 执行),在 DecorView.onAttachedToWindow 中,

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        ......
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed() && mFeatureId < 0) {
            cb.onAttachedToWindow(); // 实际是 Activity.onAttachedToWindow()
        }
        ......
    }

在3.0节 Activity.attach 的分析中,

mWindow.setCallback(this); // 设置Callback,分发点击、属性变化等事件

这行设置了 Activity 即 Callback,所以这里执行的是 Activity.onAttachedToWindow();

3:onViewAttachedToWindow() 回调,所有注册的事件处理 Listener 都保存在 ListenerInfo mListenerInfo 中,比如 OnClickListener、OnTouchListener 等,

static class ListenerInfo {
    protected OnFocusChangeListener mOnFocusChangeListener;
    private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
    private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
    public OnClickListener mOnClickListener;
    protected OnLongClickListener mOnLongClickListener;
    protected OnCreateContextMenuListener mOnCreateContextMenuListener;
    private OnKeyListener mOnKeyListener;
    private OnTouchListener mOnTouchListener;
    private OnHoverListener mOnHoverListener;
    private OnGenericMotionListener mOnGenericMotionListener;
    private OnDragListener mOnDragListener;
    private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
}

4:View.onWindowVisibilityChanged() 回调;

5.2 对窗体进行 measure

对应步骤3,执行 measureHierarchy(),即对窗体即子布局进行测量:

private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    ......
    boolean goodMeasure = false;
    if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
        ......
    }
    if (!goodMeasure) {
        childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
        childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
            windowSizeMayChange = true;
        }
    }
    ......
    return windowSizeMayChange;
}

显然,当前 Window 布局的 width 属性肯定是 MATCH_PARENT,先通过 getRootMesasureSpec 获取顶 View(DecorView)的基础大小规格:

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {

    case ViewGroup.LayoutParams.MATCH_PARENT:
        // Window can't resize. Force root view to be windowSize.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        // Window can resize. Set max size for root view.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default:
        // Window wants to be an exact size. Force root view to be that size.
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}

除非布局是 WRAP_CONTENT,这时需要对子View遍历 measure 后才能确定外,MATCH_PARENT 和确切的大小都能够立即确定。MeasureSpec.makeMeasureSpec 方法将 MeasureSpec 的类型存在32位整型的最高两位,将传入的 windowSize 存在低30位(在这里 windowSize 实际就是窗体的大小了),将结果返回。这里返回的肯定是 EXACTLY 类型,然后执行 performMeasure(),最终执行的其实就是 View.measure,也会导致 DecorView.onMeasure() 执行。

因为本文不是讲布局测量细节的,在这里我们不对测量的步骤具体展开了。

5.3 relayoutWindow

如果是首次布局,那么会执行 relayoutWindow 并请求 WMS 重新布局窗口,创建绘图表面:

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,
            mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
            mPendingConfiguration, mSurface);
    ......
    return relayoutResult;
}

这里有三个重要参数,当然在《浅析 android 应用界面的展现流程(三)会话的创建》已经分析过了:

IWindowSession mWindowSession; // 对应服务端的 Session 对象,进程单例
W mWindow; // 代表一个窗体,在 WMS 用做了 token
Surface mSurface; // 表示一个绘图表面-客户端

relayout() 的服务端实现:

public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewFlags,
        int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
        Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
    ......
    int res = mService.relayoutWindow(this, window, seq, attrs,
            requestedWidth, requestedHeight, viewFlags, flags,
            outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
            outConfig, outSurface);
    ......
    return res;
}

再来看 WindowManagerService.relayoutWindow:

public int relayoutWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int requestedWidth,
        int requestedHeight, int viewVisibility, int flags,
        Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
        Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
    ......
    long origId = Binder.clearCallingIdentity();

    synchronized(mWindowMap) {
1.      WindowState win = windowForClientLocked(session, client, false);
        if (win == null) {
            return 0;
        }
        WindowStateAnimator winAnimator = win.mWinAnimator;
        ......
        if (viewVisibility == View.VISIBLE &&
                (win.mAppToken == null || !win.mAppToken.clientHidden)) {
            ......
            try {
                if (!win.mHasSurface) {
                    surfaceChanged = true;
                }
2.              SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); // 创建绘制表面
                if (surfaceControl != null) {
3.                  outSurface.copyFrom(surfaceControl);
                    if (SHOW_TRANSACTIONS) Slog.i(TAG,
                            "  OUT SURFACE " + outSurface + ": copied");
                } else {
                    // For some reason there isn't a surface.  Clear the
                    // caller's object so they see the same state.
                    outSurface.release();
                }
            } catch (Exception e) {
                ......
            }
            ......
        } else {
            ......
        }
        ......
4.      performLayoutAndPlaceSurfacesLocked();
        ......
    }
    ......
    Binder.restoreCallingIdentity(origId);

    return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
            | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
            | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0)
            | (animating ? WindowManagerGlobal.RELAYOUT_RES_ANIMATING : 0);
}

5.3.1 windowForClientLocked

《浅析 android 应用界面的展现流程(三)会话的创建》中我们知道,WindowState 是保存在 mWindowMap(key 是客户端传过来的 W 类型的 Binder 接口)中的代表着一个客户端窗口的对象,在 addWindow 中创建并添加。WindowStateAnimator 与 WindowState 互相聚合依赖,是窗口绘制控制类。
windowForClientLocked() 其实就是从 mWindowMap 中取得对应的 WindowState 对象:

final WindowState windowForClientLocked(Session session, IBinder client,
        boolean throwOnError) {
    WindowState win = mWindowMap.get(client);
    if (win == null) {
        RuntimeException ex = new IllegalArgumentException(
                "Requested window " + client + " does not exist");
        if (throwOnError) {
            throw ex;
        }
        Slog.w(TAG, "Failed looking up window", ex);
        return null;
    }
    if (session != null && win.mSession != session) {
        RuntimeException ex = new IllegalArgumentException(
                "Requested window " + client + " is in session " +
                win.mSession + ", not " + session);
        if (throwOnError) {
            throw ex;
        }
        Slog.w(TAG, "Failed looking up window", ex);
        return null;
    }

    return win;
}

如果取到的 WindowState 为空或者与 当前的 session 不同,则会抛出 IllegalArgumentException 异常。

5.3.2 创建绘制表面

然后如果窗口可见,则会创建一个绘制表面 SurfaceControl,该绘制表面会依赖 SurfaceSession,我们知道 SurfaceSession 其实就是应用程序与 SurfaceFingor 的连接通道,SurfaceSession 中有一个 SurfaceComposerClient C++ 对象,是与 SurfaceFingor 直接通信的,展开 createSurfaceLocked 方法:

SurfaceControl createSurfaceLocked() {
    if (mSurfaceControl == null) {
        ......
        mDrawState = DRAW_PENDING;
        if (mWin.mAppToken != null) {
            if (mWin.mAppToken.mAppAnimator.animation == null) {
                mWin.mAppToken.allDrawn = false;
                mWin.mAppToken.deferClearAllDrawn = false;
            } else {
                // Currently animating, persist current state of allDrawn until animation
                // is complete.
                mWin.mAppToken.deferClearAllDrawn = true;
            }
        }

        mService.makeWindowFreezingScreenIfNeededLocked(mWin);

        int flags = SurfaceControl.HIDDEN;
        final WindowManager.LayoutParams attrs = mWin.mAttrs;

        if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
            flags |= SurfaceControl.SECURE;
        }
        ......

        int w = mWin.mCompatFrame.width(); 
        int h = mWin.mCompatFrame.height();
        if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
            // for a scaled surface, we always want the requested
            // size.
            w = mWin.mRequestedWidth;
            h = mWin.mRequestedHeight;
        }

        // Something is wrong and SurfaceFlinger will not like this,
        // try to revert to sane values
        if (w <= 0) w = 1;
        if (h <= 0) h = 1;

        mSurfaceShown = false;
        mSurfaceLayer = 0;
        mSurfaceAlpha = 0;
        mSurfaceX = 0; // 窗口坐标X
        mSurfaceY = 0; // 窗口坐标Y
        mSurfaceW = w; // 窗口宽度
        mSurfaceH = h; // 窗口高度
        mWin.mLastSystemDecorRect.set(0, 0, 0, 0);
        try {
            final boolean isHwAccelerated = (attrs.flags &
                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
            final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
            if (!PixelFormat.formatHasAlpha(attrs.format)) {
                flags |= SurfaceControl.OPAQUE;
            }
            if (DEBUG_SURFACE_TRACE) {
                ......
            } else {
                mSurfaceControl = new SurfaceControl( // 初始化 SurfaceControl
                    mSession.mSurfaceSession,
                    attrs.getTitle().toString(),
                    w, h, format, flags);
            }
            mWin.mHasSurface = true;
            ......
        } catch (OutOfResourcesException e) {
            ......
        } catch (Exception e) {
            ......
        }

        ......
        SurfaceControl.openTransaction();
        try {
            try {
                mSurfaceX = mWin.mFrame.left + mWin.mXOffset;
                mSurfaceY = mWin.mFrame.top + mWin.mYOffset;
                mSurfaceControl.setPosition(mSurfaceX, mSurfaceY);
                mSurfaceLayer = mAnimLayer; // 窗口 Z 轴位置
                mSurfaceControl.setLayerStack(mLayerStack);
                mSurfaceControl.setLayer(mAnimLayer);
                mSurfaceControl.setAlpha(0);
                mSurfaceShown = false;
            } catch (RuntimeException e) {
                ......
            }
            mLastHidden = true;
        } finally {
            SurfaceControl.closeTransaction();
            .......
        }
        ......
    }
    return mSurfaceControl;
}

可以看出,我们在创建完绘图表面 SurfaceControl 后,通过 openTransaction 和 clossTransaction 来控制绘制事务,这里我们就不再细说了,在下一篇文章中分析布局、绘制过程时会详细展开,这里在事务期间会对绘制表面的位置、大小、Z轴位置进行控制,一些关键的字段已经标注了注释了,我们直接来看 SurfaceControl 的构造函数:

public SurfaceControl(SurfaceSession session,
        String name, int w, int h, int format, int flags)
                throws OutOfResourcesException {
    if (session == null) {
        throw new IllegalArgumentException("session must not be null");
    }
    if (name == null) {
        throw new IllegalArgumentException("name must not be null");
    }
    ......

    checkHeadless();

    mName = name;
    mNativeObject = nativeCreate(session, name, w, h, format, flags);
    if (mNativeObject == 0) {
        throw new OutOfResourcesException(
                "Couldn't allocate SurfaceControl native object");
    }

    mCloseGuard.open("release");
}

其中重要的一步就是通过 nativeCreate 创建一个 C++ 层的 SurfaceControl,当然创建它少不了 SurfaceSession 中的 SurfaceComposerClient,所以把 SurfaceSession 也作为参数传入了,nativeCreate 的实现是在 /framworks/base/
core/jni/android_view_SurfaceControl.cpp:

static jint nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags) {
    ScopedUtfChars name(env, nameStr);
1.  sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
2.  sp<SurfaceControl> surface = client->createSurface(
            String8(name.c_str()), w, h, format, flags);
    if (surface == NULL) {
        jniThrowException(env, OutOfResourcesException, NULL);
        return 0;
    }
    surface->incStrong((void *)nativeCreate);
    return int(surface.get());
}

1.client 即 SurfaceComposerClient 对象的指针,展开 android_view_SurfaceSession_getClient:

sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
        JNIEnv* env, jobject surfaceSessionObj) {
    return reinterpret_cast<SurfaceComposerClient*>(
            env->GetIntField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
}

在《浅析 android 应用界面的展现流程(三)会话的创建》中我们知道,SurfaceSession 中通过 nativeCreate JNI 方法创建并获取 SurfaceComposerClient 对象的指针,在java 层保存在 mNativeClient 中,android_view_SurfaceSession_getClient() 方法其实就是取得 SurfaceSession 中的这个 mNativeClient,然后强转为 SurfaceComposerClient,由此获取 SurfaceComposerClient 对象的指针。gSurfaceSessionClassInfo.mNativeClient 其实是在注册 JNI 函数时就已经赋值了:

int register_android_view_SurfaceSession(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "android/view/SurfaceSession",
            gMethods, NELEM(gMethods));
    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");

    jclass clazz = env->FindClass("android/view/SurfaceSession");
    gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "I");
    return 0;
}

2.拿到 SurfaceComposerClient 对象后,通过 createSurface 方法来创建 C++ 层的 SurfaceControl,然后返回给 java 层:


sp<SurfaceControl> SurfaceComposerClient::createSurface(
        const String8& name,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        uint32_t flags)
{
    sp<SurfaceControl> sur;
    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;
        status_t err = mClient->createSurface(name, w, h, format, flags,
                &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            sur = new SurfaceControl(this, handle, gbp);
        }
    }
    return sur;
}

其中 mClient 就是与 Surfaceflinger 的连接(Binder 对象)客户端了,后续的 Surfaceflinger 相关的文章会再对 createSurface 展开说明。

5.3.3 SurfaceControl -> Surface

在完成绘制表面的创建后,会将其拷贝至应用程序所在进程中的 Surface 对象中,即 outSurface.copyFrom(surfaceControl),outSurface 就是应用程序端执行 relayout 时传过来的 Surface 对象(实现了 Parcelable),outSurface 在 relayout 接口声明时为 out 类型的:

int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
        int requestedWidth, int requestedHeight, int viewVisibility,
        int flags, out Rect outFrame, out Rect outOverscanInsets,
        out Rect outContentInsets, out Rect outVisibleInsets,
        out Configuration outConfig, out Surface outSurface);

说明这个参数是纯粹的作为返回的参数传入了,那么在服务端函数执行结束后会执行 outSurface.writeToParcel,客户端在函数结束后(读取异常后)会执行 outSurface.readFromParcel 还原写入的结果,关于 AIDL 和接口参数定义的相关内容大家可以参考官网

copyFrom 实际是取出 native 层的 SurfaceControl 中的 Surface 对象:

<center>

copyfrom.jpg

图 5.2 copyFrom 调用流程</center>
copyFrom 的结果就是 Surface 的字段 mNativeObject 被赋值为对应的 native Surface 对象的指针,在relayout 执行结束后会调用 outSurface.writeToParcel() 将该指针对象写入服务端:

public void writeToParcel(Parcel dest, int flags) {
    if (dest == null) {
        throw new IllegalArgumentException("dest must not be null");
    }
    synchronized (mLock) {
        dest.writeString(mName);
        nativeWriteToParcel(mNativeObject, dest);
    }
    if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
        release();
    }
}

应用程序一端读取该对象,将 mNativeObject 还原:

public void readFromParcel(Parcel source) {
    if (source == null) {
        throw new IllegalArgumentException("source must not be null");
    }
    synchronized (mLock) {
        mName = source.readString();
        setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
    }
}

然后剩下relayoutWindow() 的最后一步,即刷新系统UI,performLayoutAndPlaceSurfacesLocked():

private final void performLayoutAndPlaceSurfacesLocked() {
    int loopCount = 6;
    do {
        mTraversalScheduled = false;
        performLayoutAndPlaceSurfacesLockedLoop();
        mH.removeMessages(H.DO_TRAVERSAL);
        loopCount--;
    } while (mTraversalScheduled && loopCount > 0);
    mInnerFields.mWallpaperActionPending = false;
}

可以发现它其实是循环最多 7 次调用 performLayoutAndPlaceSurfacesLockedLoop,它又调用了 performLayoutAndPlaceSurfacesLockedInner,这个近 600 行的大函数执行了对系统 UI 上所有窗口的大小计算和界面刷新操作,Activity窗口在其属性发生了变化,例如,可见性、大小发生了变化,又或者它新增、删除了子视图,都需要重新计算大小,这些都需要 WMS 通过这个函数去刷新 UI,这个函数的具体流程我们会在多篇文章中详细展开。

小结

本文主要将绘制表面的创建的流程过了一遍,一句话概括就是:dipatchAttach -> measure -> relayout,Surface - SurfaceControl。

下面两篇文章将分别对
步骤7:performLayout 进行窗体布局;
步骤8:performDraw 进行绘制
分别展开说明,标题号仍会在本节内,从 5.4 开始。

标签: none

添加新评论