开发编写的 View 控件,是怎么变成屏幕上图像的?

  1. Activity 与 Window
    1. setContentView 原理
    2. Activity 窗口添加过程
  2. UI 刷新机制
    1. 发起 UI 重绘请求
    2. 执行 UI 绘制时机
    3. vsync 信号的生成
    4. vsync 信号的分发
      1. SurfaceFlinger -> DispSync
      2. DispSync -> DispSyncSource
      3. DispSyncSource -> EventThread
      4. Connection -> SurfaceFlinger
      5. Connection -> app
  3. UI 绘制流程
  4. Surface 原理
    1. Surface 创建流程
    2. Surface 的跨进程传递
    3. GraphicBufferProducer

1. Activity 与 Window

1. setContentView 原理

Activity 中将 setContentView 工作交给了 Window 对象去处理:

1
2
3
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
}

每个 Activity 内部都持有一个 Window 对象,而 Window 对象是在创建 Activity 流程中 attach 时就实例化的:

1
2
3
final void attach(...){
mWindow = new PhoneWindow(this, window, activityConfigCallback);
}

PhoneWindow 中会实际去 inflate 布局:

1
2
3
4
5
6
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
}

而 installDecor 中伪代码逻辑如下:

1
2
3
4
5
6
7
8
private void installDecor() {
if (mDecor == null) {
mDecor = new DecorView(...);
}
if (mContentParent == null) {
mContentParent = (ViewGroup)mDecor.findViewById(R.id.content);
}
}

每个 Activity 内部都持有一个 window 对象,其实现为 PhoneWindow,在 Activity attachContext 时创建。

PhoneWindow 的根布局为 DecorView,其包括 TitleView 和 ContentView,Activity 的 setContentView 就是把布局添加到 ContentView。

2. Activity 窗口添加过程

setContentView 只是创建好了 View 视图结构,还没告知 WMS。在 ActivityThread handleResumeActivity 方法中,才会与 WMS 通信开始添加 Activity 窗口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void handleResumeActivity(IBinder token, ...) {
final ActivityClientRecord r = performResumeActivity(token, ...);
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
a.mDecor = decor;
wm.addView(decor, l);
}
r.activity.makeVisible();
}

其中 performResumeActivity 方法会回调 onResume 生命周期,之后调用 WindowManager 的 addView 方法并将 DecorView 传入。这里的 WindowManager 为 WindowManagerImpl 对象,与 Activity 内部的 window 一样,都是在 Activity attachContext 时创建。

WindowManagerImpl 中 addView 方法如下:

1
2
3
4
5
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

其进一步交给了 mGlobal 即 WindowManagerGlobal 去处理,接着来看 WindowManagerGlobal 的 addView 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}

由于 WindowManagerGlobal 为进程单例,其内部的 mViews 则记录了全局添加的 View。当重复添加 View 时,就会抛出 “View has already been added to the window manager” 异常。

接着创建一个与 View 对应的 ViewRootImpl,将 View、ViewRootImpl 记录在 WindowManagerGlobal 中后,调用了 ViewRootImpl 的 setView 方法。先来看 ViewRootImpl 的构造函数:

1
2
3
4
5
6
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);
...
}

其中 mWindowSession 是通过 WMS openSession 获取的匿名 binder,用于应用调用 WMS;mWindow 也是一个 binder 接口,用于 WMS 调用应用端。

接着看 ViewRootImpl setView 方法,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
requestLayout();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
}
}
}

值得注意的是,不需把 view 传给 WMS,这是因为 WMS 并不关心 View 树所表达的具体 UI 内容,它只要知道各应用进程显示界面的大小、窗口层级值即可。

到达 WMS 所在 system_server 进程后,WindowSession addToDisplay 会进一步调用 WindowManagerService 的 addWindow 方法,执行添加用户窗口工作,包括:

  • 对用户窗口进行权限检查,比如 TYPE_PHONE 等窗口类型需要 SYSTEM_ALERT_WINDOW 权限
  • 检查 mWindow,窗口最终会记录到 <IBinder,WindowState> HashMap 中,其中 IBinder 为应用端的 mWindow,即一个 mWindow 只允许添加唯一的窗口
  • 检查窗口类型,比如子窗口必须依赖于一个父窗口
  • 按照窗口层级添加合适的位置
  • 等等…

小结一下,当 Activity 第一次回调 onResume 后,将 Activity 对应的窗口添加到 WMS 的过程:

  • 首先调用了 WindowManagerImpl,WindowManagerImpl 进一步调用进程单例的 WindowManagerGlobal
  • WindowManagerGlobal 中创建了与 DecorView 对应的 ViewRootImpl,并将 DecorView 和 ViewRootImpl 记录下来
  • WindowManagerImpl 和 WindowManagerGlobal 都还在应用进程,与 WMS 没什么关系
  • 在 ViewRootImpl 中与 WMS 发生交互,应用端通过 WindowSession 调用 WMS,WMS 通过 IWindow 调用应用端
  • WMS 中会对窗口进行权限、类型等检查,最终将应用窗口信息记录下来

2. UI 刷新机制

1. 发起 UI 重绘请求

ViewRootImpl 中的 performTraversals 中会依次调用 performMeasure、performLayout、performDraw,分别对应于 measure、layout、draw,由顶而下的进行界面绘制逻辑。

调用 View 控件 requestLayout、invalidate 等方法请求 UI 重绘时,会统一调用到 ViewRootImpl 的 scheduleTraversals 方法,代码如下:

1
2
3
4
5
6
7
8
9
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}

其中 postSyncBarrier 插入一个消息屏障 block 普通消息,以保证主线程可以优先来执行接下来的绘制工作。mTraversalRunnable 的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

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

void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}

通过 mTraversalScheduled 变量可以看出,不是每次调用 requestLayout、invalidate 方法就会触发一次 UI 重绘的,而是要等 mTraversalRunnable 被执行后才会接收下一次的重绘请求。

在 mTraversalRunnable 中调用了 performTraversals() 进行真正的 UI 绘制,而 UI 真正绘制的时机则取决于 mChoreographer 触发回调的时机。

2. 执行 UI 绘制时机

ViewRootImpl 接收 UI 重绘请求后,将真正的 UI 绘制时机交给了 Choreographer,而 Choreographer 中会在每次 vsync 信号到来时执行 UI 绘制。

调用 Choreographer 的 postCallback 方法将 UI 绘制 TraversalRunnable 传入后,会进一步调用 Choreographer 的 postCallbackDelayedInternal 方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}

首先将 UI 绘制 action 记录到 mCallbackQueues 队列中,然后根据处理时间决定立即调用 scheduleFrameLocked ,或发送异步消息延时调用 scheduleFrameLocked。

scheduleFrameLocked 方法关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
private void scheduleFrameLocked(long now) {
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
}

如注释所示,scheduleFrameLocked 中需要切换到指定线程中调用 scheduleVsyncLocked:

1
2
3
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}

scheduleVsync 表示要接受下一次 vsync 信号,等到 vsync 信号到来时会由 SurfaceFlinger 回调通知。直接来看 Choreographer 接受到 vsync 信号后的处理,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
}
}
...
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}

当要绘制的图像时间戳晚于一个帧刷新周期时,会去进一步计算异常跳过的帧数,如果跳过的帧数过大,就可以看到非常眼熟的一条日志了:“Skipped xx frames! The application may be doing too much work on its main thread”

随后通过 doCallbacks 回调触发执行 UI 绘制,也就是执行 ViewRootImpl 传过来的 TraversalRunnable、调用 performTraversals 方法,由顶而下的执行界面绘制逻辑。

3. vsync 信号的生成

vsync 信号在 SurfaceFlinger 中生成,并且有硬件、软件两种生成方式 — 仅通过这种高度总结的描述,我们能得到多少信息呢? 具体是如何生成?是优先选硬件生成还是软件生成还是同时存在?

下面就通过源码来一探究竟,既然在 SurfaceFlinger 中生成,那先来看 SurfaceFlinger 的 init 方法:

1
2
3
4
5
6
void SurfaceFlinger::init() {
...
mHwc = new HWComposer(this);
mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
...
}

HWComposer 是 vsync 信号实际生成的类,SurfaceFlinger 通过 setEventHandler 向 HWComposer 注册了一个回调,使 SurfaceFlinger 能够接受由 HWComposer 生成的 vsync 信号,EventHandler 中的回调方法如下:

1
virtual void onVSyncReceived(int32_t disp, nsecs_t timestamp)

下面来看实际生成 vsync 的 HWComposer,其构造方法关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
HWComposer::HWComposer(...){
bool needVSyncThread = true;
loadHwcModule(); //加载硬件
if(mHwc){ //硬件加载成功
mCBContext->procs.vsync = &hook_vsync; //注册硬件源回调
mHwc->registerProcs(mHwc, &mCBContext->procs);
needVSyncThread = false; //不使用软件方式
}
if(needVSyncThread){
//如果硬件不支持,则用软件模拟
mVSyncThread = new VSyncThread(*this);
}
}

逻辑十分简单,首先去加载硬件,优先使用硬件生成方式,如果硬件不支持,则用软件模拟。

硬件源生成的回调 hook_vsync 方法调用如下:

1
2
3
4
5
6
7
8
9
10
void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
int64_t timestamp) {
cb_context* ctx = reinterpret_cast<cb_context*>(
const_cast<hwc_procs_t*>(procs));
ctx->hwc->vsync(disp, timestamp); //调用 vsync 方法
}

void HWComposer::vsync(int disp, int64_t timestamp) {
mEventHandler.onVSyncReceived(disp, timestamp); //回调出去
}

至此 HWComposer 中的 vsync 硬件生成方式流程完毕,最后调用了 mEventHandler 回调,交由 SurfaceFlinger 处理。

再来看软件模拟生成方式,其通过 VSyncThread 类实现,VSyncThread 继承了 Thread 类,自然也继承了 Thread 类的 threadLoop 方法。

如果 threadLoop 方法返回 true,那当工作线程启动后,基类 Thread 会循环的调用 threadLoop 方法。而软件模拟生成的 vsync 信号就是在 threadLoop 方法中生成的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool HWComposer::VSyncThread::threadLoop() {
const nsecs_t period = mRefreshPeriod; // vsync 周期
const nsecs_t now = systemTime(CLOCK_MONOTONIC); //当前时间
nsecs_t next_vsync = mNextFakeVSync; //本次要生成的 vsync 信号时间
nsecs_t sleep = next_vsync - now;
if (sleep < 0) {
// 错过了,重新计算本次要生成的 vsync 信号时间
sleep = (period - ((now - next_vsync) % period));
next_vsync = now + sleep;
}
mNextFakeVSync = next_vsync + period; // 下一个 vsync 信号时间
struct timespec spec;
spec.tv_sec = next_vsync / 1000000000;
spec.tv_nsec = next_vsync % 1000000000;
int err;
do {
//休眠到 next_vsync 即本次 vsync 信号时间
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err<0 && errno == EINTR);
if (err == 0) {
mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
}
return true; //让系统循环调用此方法
}

如何在指定的时间点产生信号呢?如代码所示,在每次循环时采用休眠的方式主动等待至 vsync 时间点。

第一次执行时, next_vsync、mNextFakeVSync 都为 0,所以 sleep < 0 进而会重新计算 next_vsync 为 now + period,也就是基于当前时间再等一个 vsync 周期。

后续每次循环也会重新修正 vsync 信号的时间,避免回调 onVSyncReceived 方法或其他耗时操作导致 vsync 信号时间变得越来越晚。

小结一下,vsync 信号在 SurfaceFlinger 进程的 HWComposer 类中生成,优先使用硬件源生成方式,如果硬件不支持则使用软件方式模拟生成。不管是硬件生成还是软件生成,都会通过相同的回调统一通知,最后交由 SurfaceFlinger 进一步处理。

4. vsync 信号的分发

1. SurfaceFlinger -> DispSync

vsync 信号由 HWComposer 类生成后,会回调 SurfaceFlinger.cpp 的 onVSyncReceived 方法:

1
2
3
void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) {
mPrimaryDispSync.addResyncSample(timestamp);
}

mPrimaryDispSync 为 DispSync 类型,DispSync 在构造时开启了一个 DispSyncThread 工作线程,专门负责 vsync 信号的分发:

1
2
3
4
5
6
7
DispSync::DispSync(const char* name) :
mName(name),
mRefreshSkipCount(0),
mThread(new DispSyncThread(name)) {
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
...
}

接着看 DispSync 的 addResyncSample 方法是如何处理 vsync 信号的:

1
2
3
4
5
6
7
8
bool DispSync::addResyncSample(nsecs_t timestamp) {
mResyncSamples[idx] = timestamp; //vsync 信号时间戳
updateModelLocked();
}

void DispSync::updateModelLocked() {
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}

DispSync 内部记录下 vsync 信号时间戳后,通过 updateModel 方法通知内部工作线程 DispSyncThread:

1
2
3
4
5
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
Mutex::Autolock lock(mMutex); //锁
mPeriod = period;
mCond.signal(); //唤醒
}

注意当前还处于 vsync 信号生成线程,若是软件生成则还处于 HWComposer 的 VSyncThread 线程,为了不影响 vsync 信号的生成工作,这里切换到另外的线程去分发。

具体而言,就是通过 mCond.signal() 方法通知 DispSyncThread 线程去进一步分发,DispSyncThread 的分发工作在其 threadLoop 方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
virtual bool threadLoop() {
while (true) {
{
Mutex::Autolock lock(mMutex); //锁
if (mPeriod == 0) {
mCond.wait(mMutex); //没 vsync 信号就阻塞
continue;
}
now = systemTime(SYSTEM_TIME_MONOTONIC);
// 收集注册了需要接受 vsync 信号的回调
callbackInvocations = gatherCallbackInvocationsLocked(now);
}
if (callbackInvocations.size() > 0) {
//分发回调
fireCallbackInvocations(callbackInvocations);
}
}
}

至此 DispSync 中对 vsync 信号的分发工作执行完毕。

2. DispSync -> DispSyncSource

在 DispSync 内部工作线程 DispSyncThread 中,对注册需要接受 vsync 信号的回调进行调用,完成了 vsync 信号分发工作。

那具体是谁注册了监听?是谁需要进一步接受 vsync 信号呢?还是要从 DispSyncThread 的 threadLoop 方法入手,它通过 gatherCallbackInvocationsLocked 方法获取到当前已注册的监听,代码如下:

1
2
3
4
5
6
7
8
9
Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
Vector<CallbackInvocation> callbackInvocations;
for (size_t i = 0; i < mEventListeners.size(); i++) {
CallbackInvocation ci;
ci.mCallback = mEventListeners[i].mCallback;
callbackInvocations.push(ci);
}
return callbackInvocations;
}

然后通过 fireCallbackInvocations 方法回调了监听:

1
2
3
4
5
void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
}

DispSync 内部对监听进行了封装,我们只关注外部传来的 mCallback,其为 DispSync::Callback 类型。通过以上代码可以知道 mCallback 被记录在 mEventListeners 中,那只要再看下哪里将监听添加到 mEventListeners 中,就能知道到底是谁注册的监听了。

在 SurfaceFlinger 初始化时 DispSyncSource 与 DispSync(mPrimaryDispSync) 产生关联:

1
2
3
4
5
6
7
8
9
10
void SurfaceFlinger::init() {
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc, *this);

sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this);
...
}

也就是说 DispSyncSource 内部持有 DispSync,在 DispSyncSource 内部将监听注册到 DispSync 中:

1
2
3
4
5
6
7
8
9
10
11
virtual void setVSyncEnabled(bool enable) {
Mutex::Autolock lock(mVsyncMutex);
if (enable) {
status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
static_cast<DispSync::Callback*>(this));
} else {
status_t err = mDispSync->removeEventListener(
static_cast<DispSync::Callback*>(this));
}
mEnabled = enable;
}

DispSyncSource 本身继承了 DispSync::Callback 接口,即直接将自身作为监听注册到 DispSync 中。

也就是说,在 DispSync 内部工作线程中通过 fireCallbackInvocations 分发 vsync 信号,实际上调用了 DispSyncSource 的 onDispSyncEvent 方法。

需要注意的是,SurfaceFlinger init 时创建了两个 DispSyncSource,分别是上层 app 和 surfaceFlinger 自身,为什么要分这两种呢?仅仅是代码上的逻辑分离吗?

仔细观察 SurfaceFlinger init 中对这两个 DispSyncSource 的构造入参,分别传入了 vsyncPhaseOffsetNs 和 sfVsyncPhaseOffsetNs,这是要实现 vsync 信号的错时分发,即不同时分发 vsync 信号给上层 app 和 surfaceFlinger。

3. DispSyncSource -> EventThread

接着来看 DispSyncSource 的 onDispSyncEvent 方法是如何处理 vsync 信号的:

1
2
3
4
5
6
7
8
9
10
virtual void onDispSyncEvent(nsecs_t when) {
sp<VSyncSource::Callback> callback;
{
Mutex::Autolock lock(mCallbackMutex);
callback = mCallback;
}
if (callback != NULL) {
callback->onVSyncEvent(when);
}
}

又是一个 callBack 回调了出去,这个 callBack 又是谁呢?可以再看一下上面的 SurfaceFlinger init 方法,类似于 DispSyncSource 向 DispSync 注册监听,EventThread 构造时传入了 DispSyncSource,EventThread 向 DispSyncSource 注册了监听。

callBack 类型为 VSyncSource::Callback,而 EventThread 自身也实现了这个接口:

1
class EventThread : public Thread, private VSyncSource::Callback

接着来看 EventThread 内部实现的 onVSyncEvent 方法:

1
2
3
4
5
6
7
8
void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
mVSyncEvent[0].header.id = 0;
mVSyncEvent[0].header.timestamp = timestamp; //vsync 时间戳
mVSyncEvent[0].vsync.count++;
mCondition.broadcast();
}

注意当前还处于 DispSync 的工作线程,这里将 vsync 信号记录到 DisplayEventReceiver::Event 类型的 mVSyncEvent 数组中后,通过 mCondition.broadcast() 通知 EventThread 工作线程去处理,即从 DispSync 工作线程切换到了 EventThread 工作线程。

下面来看 EventThread 的工作线程中是如何处理的:

1
2
3
4
5
6
7
8
9
10
11
bool EventThread::threadLoop() {
DisplayEventReceiver::Event event;
Vector< sp<EventThread::Connection> > signalConnections;
signalConnections = waitForEvent(&event);
const size_t count = signalConnections.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Connection>& conn(signalConnections[i]);
status_t err = conn->postEvent(event);
}
return true;
}

同样是通过返回 true 来实现线程无限循环调用 threadLoop 方法,其中比较关键的是 waitForEvent 方法,关键逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
DisplayEventReceiver::Event* event){
Vector< sp<EventThread::Connection> > signalConnections;
do {
检测 mVSyncEvent 数组中是否有 vsync 信号
如果有传递给 event 参数,并开始遍历所有注册的 connection
如果 connection.count >= 0,就加到 signalConnections 中
if(waitForVSync){
mCondition.waitRelative(mLock, timeout); //休眠等待
}
} while (signalConnections.isEmpty());
return signalConnections;
}

梳理一下 EventThread 工作线程中的逻辑:通过 waitForEvent 方法进入休眠等待 vsync 信号,如果有 vsync 信号会唤醒,并将 vsync 信号传递给 event,返回 Connection,随后将 vsync 信号分发给了 Connection。

现在我们的关注点变为 EventThread::Connection,它是谁注册的?其 postEvent 方法又是如何进一步分发 vsync 信号的呢?

4. Connection -> SurfaceFlinger

依然聚焦于 vsync 的传递为脉络,接着来看 Connection 的 postEvent 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
status_t EventThread::Connection::postEvent(
const DisplayEventReceiver::Event& event) {
DisplayEventReceiver::sendEvents(mChannel, &event, 1);
}

ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
Event const* events, size_t count){
return BitTube::sendObjects(dataChannel, events, count);
}

ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
void const* events, size_t count, size_t objSize){
const char* vaddr = reinterpret_cast<const char*>(events);
ssize_t size = tube->write(vaddr, count*objSize);
}

可以看到 Connection 内部持有一个 BitTube,BitTube 是用来处理进程间通讯的机制,和管道类似,基于 SocketPair 封装实现。SocketPair 用来创建一对未命名、互相连接的套接字,套接字的一端可以进行读和写的操作,用来实现全双工的通讯。

在 Connection 中将 vsync 信号数据写入了 BitTube,那在哪里监听读呢?这就要分 SurfaceFlinger 与 app 两路了,首先来看 SurfaceFlinger 是如何接收的。再回到 SurfaceFlinger 的 init 方法:

1
2
3
4
5
6
7
8
9
10
11
void SurfaceFlinger::init() {
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc, *this);

sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this);
mEventQueue.setEventThread(mSFEventThread);
...
}

在构造完 SurfaceFlinger 的 EventThread 后,通过 setEventThread 方法将 EventThread 设置给 mEventQueue,mEventQueue 为 MessageQueue 类型,方法如下:

1
2
3
4
5
6
7
void MessageQueue::setEventThread(const sp<EventThread>& eventThread){
mEventThread = eventThread;
mEvents = eventThread->createEventConnection();
mEventTube = mEvents->getDataChannel();
mLooper->addFd(mEventTube->getFd(), 0, Looper::EVENT_INPUT,
MessageQueue::cb_eventReceiver, this);
}

这下就全走通了,SurfaceFlinger 的消息队列监听了 Connection 中的 BitTube,当 vsync 信号在 EventThread 中分发给 Connection 写入 BitTube 后,SurfaceFlinger 的消息队列就能收到通知了。

接着来看 SurfaceFlinger 的 MessageQueue 如何进一步处理 vsync 消息,在 MessageQueue 的 setEventThread 方法中设置接受到 vsync 后回调 cb_eventReceiver 方法:

1
2
3
4
int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
return queue->eventReceiver(fd, events);
}

cb_eventReceiver 进一步调用 eventReceiver 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
#if INVALIDATE_ON_VSYNC
mHandler->dispatchInvalidate();
#else
mHandler->dispatchRefresh();
#endif
break;
}
}
}
return 1;
}

可以看到读取出 vsync 信号数据后,只用到了 type 信息,然后通过 Handler 回调出去处理。

至此 vsync -> SurfaceFlinger 的分发流程分析完毕。

5. Connection -> app

尽管 vsync 传递给 SurfaceFlinger 流程发生在同个进程,却使用了支持跨进程的 BitTube。为什么要使用 BitTube 呢?可以想到的一个优点是,可以与 vsync 信号传递给上层 app 进程的方式统一。

我们知道上层 app 会统一将 UI 刷新请求发给 Choreographer,Choreographer 会在下次 vsync 信号到来时真正执行 UI 绘制,下面通过源码来搞懂底层的 vsync 是如何传递至 Choreographer 的。

Choreographer 中实际与 vsync 信号相关的逻辑在 DisplayEventReceiver 中,初始化 Choreographer 时会构造 DisplayEventReceiver:

1
2
3
4
5
6
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = new FrameDisplayEventReceiver(looper, vsyncSource);
...
}

DisplayEventReceiver 构造函数中会调用到 DisplayEventReceiver.java 的 nativeInit 方法:

1
2
3
private static native long nativeInit(
WeakReference<DisplayEventReceiver> receiver,
MessageQueue messageQueue, int vsyncSource);

底层实现位于 android_view_DisplayEventReceiver.cpp 中:

1
2
3
4
5
6
7
8
9
10
static jlong nativeInit(JNIEnv* env, 
jclass clazz,
jobject receiverWeak, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
receiverWeak, messageQueue);
status_t status = receiver->initialize();
...
return reinterpret_cast<jlong>(receiver.get());
}

构造了一个 NativeDisplayEventReceiver,其继承自 DisplayEventReceiver,在后者的构造函数中,创建了关键的 IDisplayEventConnection binder 接口,建立与 SurfaceFlinger 通信的连接:

1
2
3
4
5
6
7
8
9
10
11
DisplayEventReceiver::DisplayEventReceiver() {
// ISurfaceComposer binder 接口
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != NULL) {
// IDisplayEventConnection binder 接口
mEventConnection = sf->createDisplayEventConnection();
if (mEventConnection != NULL) {
mDataChannel = mEventConnection->getDataChannel();
}
}
}

为什么要将 MessageQueue 传递下来呢?因为要借助 Android Handler 已有的功能,即基于内部 epoll 机制添加对某文件描述符事件的监听,上文传递给 SurfaceFlinger 的原理也是如此。

nativeInit 中构造 NativeDisplayEventReceiver 后调用了其 initialize 方法,内部便通过 Looper 添加了对文件描述符的监听:

1
2
3
4
status_t DisplayEventDispatcher::initialize() {
int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
return OK;
}

然后再回到上层的 Choreographer ,其通过 scheduleVsync 请求下一次的 vsync 信号,最终会调用到 DisplayEventReceiver 的 native 方法:

1
2
3
4
5
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}

private static native void nativeScheduleVsync(long receiverPtr);

底层同样是在 android_view_DisplayEventReceiver.cpp 中:

1
2
3
4
5
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
sp<NativeDisplayEventReceiver> receiver =
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
receiver->scheduleVsync();
}

最终调用到 DisplayEventReceiver 的 requestNextVsync 方法:

1
2
3
status_t DisplayEventReceiver::requestNextVsync() {
mEventConnection->requestNextVsync();
}

上面提到 mEventConnection 是一个 binder 接口,跨进程调用到 BnDisplayEventConnection,而后者的实现类就是 SurfaceFlinger 中的 EventThread:

1
2
class EventThread : public Thread, private VSyncSource::Callback {
class Connection : public BnDisplayEventConnection {...}

所以会进一步调用到 EventThread 的 requestNextVsync 方法:

1
2
3
4
5
6
7
8
9
void EventThread::requestNextVsync(
const sp<EventThread::Connection>& connection) {
Mutex::Autolock _l(mLock);
mFlinger.resyncWithRateLimit();
if (connection->count < 0) {
connection->count = 0;
mCondition.broadcast();
}
}

上文对 EventThread 做过详细的分析,这里再贴一遍其工作线程的关键逻辑代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
DisplayEventReceiver::Event* event){
Vector< sp<EventThread::Connection> > signalConnections;
do {
检测 mVSyncEvent 数组中是否有 vsync 信号
如果有传递给 event 参数,并开始遍历所有注册的 connection
如果 connection.count >= 0,就加到 signalConnections 中
if(waitForVSync){
mCondition.waitRelative(mLock, timeout); //休眠等待
}
} while (signalConnections.isEmpty());
return signalConnections;
}

如上,在 requestNextVsync 中将 connection->count 置为 0 然后唤醒 EventThread,EventThread 被唤醒后会将 connection 加入要通知 vsync 的 signalConnections 中,随后与上文中分析过的逻辑一致,通过 BitTube+looper 进一步分发 vsync 信号。

值得注意的是,在 waitForEvent 中判断如果 connection->count 为 0,会将 count 置为 -1,这意味着 requestNextVsync 方法名副其实,只是一次性请求接受一次 vsync 信号而已,要再调用 requestNextVsync 才能再次接受 vsync 信号。

最后再来看 vsync 信号传递到 java 层的流程,vsync 消息到来通过 dispatchVsync 方法分发:

1
2
3
4
5
6
7
8
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
...
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
mWaitingForVsync = false;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
return 1;
}

然后又回到了与 java 层最接近的 android_view_DisplayEventReceiver.cpp 中:

1
2
3
4
5
6
7
8
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
env->CallVoidMethod(receiverObj.get(),
gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
}
}

jni 调用了上层 DisplayEventReceiver.java 的 dispatchVsync 方法:

1
2
3
4
5
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}

随后进一步回调到 Choreographer 的 doFrame,执行 ViewRootImpl 传过来的 TraversalRunnable、调用 performTraversals 方法,由顶而下的执行界面绘制逻辑。

小结一下:vsync 信号分为 SurfaceFlinger、app 两路分发,这两路分别对应于一个 EventThread。当 vsync 信号到来时,会唤醒 EventThread 线程,然后 EventThread 通过注册到内部的 Connection 分发出去。Connection 进一步通过 BitTube 并使用消息机制 looper 监听 BitTube 的 fd,从而实现 vsync 的分发。

3. UI 绘制流程

4. Surface 原理

1. Surface 存在的意义

Android GUI 系统中有两类本地窗口,FramebufferNativeWindow 和 Surface。FramebufferNativeWindow 面向 SurfaceFlinger,使用了帧缓冲区,用于直接显示在屏幕上;而 Surface 面向应用程序,是从内存缓冲区分配的空间。

为什么要有面向应用程序的图像缓冲区 Surface 呢?只有屏幕显示帧缓冲区不可以吗?假如系统中只有一个需要显示 UI 的程序,仅有帧缓冲区确实可以胜任,但如果有多个 UI 程序的话,每个程序都要使用 FrameBuffer,这就像幼儿园的几个小朋友共用一个画板来涂鸦一样,”五彩斑斓”、”创意无限”。

所以需要有面向应用程序的 Surface,供应用程序独立的准备其图像数据。SurfaceFlinger 会收集所有应用程序的的 Surface,对它们进行统一的混合操作,然后输出到屏幕显示帧缓冲区上,从而保证多个应用程序图像可以被有序的显示到屏幕上。

2. Surface 创建流程

3. GraphicBufferProducer

1. BufferQueue

1. Canvas 和 Surface

window surface Layer BufferQueue 关系

skia 和 OpenGL