Choreographer 编舞者(一)

关于Choreographer的知识,这里分作3部分来解析:

  1. Choreographer的初始化
  2. View绘制请求
  3. View绘制

先放一张总流程图,如下:


choreographer_sequence.png

第一部分 Choreographer的初始化

//ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
        ...
        mChoreographer = Choreographer.getInstance();
        ...
    }

初始化从ViewRootImpl开始,在创建ViewRootImpl的时候会初始化Choreography。

//Choreographer.java
    public static Choreographer getInstance() {
        return sThreadInstance.get();
    }
//Choreographer.java
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = choreographer;
            }
            return choreographer;
        }
    };

这里有几点需要注意:

  1. ThreadLocal,表示Choreography是线程私有的,也就是每一个ViewRoot都会私有一个Choreography实例,二者是一一对应的
  2. VSYNC_SOURCE_APP,这个是用来与Vsync源建立连接的,其定义在ISurfaceComposer.h中,目前有两类,一个是app,一个是sf(SurfaceFlinger),他们会分别监听响应VSync信号,后面还会讲到
  3. looper,发起创建Choreography的线程必须有自己的Looper,因为后续VSync信号来临时需要通过handler的方式发给各自的对应的thread

顺便可以了解一下如下几个概念的对应关系,对这些概念有个大概的轮廓印象:


绘图1.png
//Choreographer.java
    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;

        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }

note:
USE_VSYNC: 默认为true,可以通过设置系统属性debug.choreographer.vsync为false来关闭,只用于调试
mFrameIntervalNanos: 其实就是VSync周期时间,单位纳秒。
mCallbackQueues:这个是记录回调的,Vsync信号来临时,会通过这个回调去通知相应目标,后续在设置回调的地方会详细描述。
FrameDisplayEventReceiver:这个是初始化比较重要的一环,初始化DisplayEventReceiver来监听底层VSync信号,FrameDisplayEventReceiver是Choreography内部类,继承自DisplayEventReceiver,所以这里实际上是调用的DisplayEventReceiver的构造函数。

//DisplayEventReceiver.java
    public DisplayEventReceiver(Looper looper, int vsyncSource) {
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
                vsyncSource);
    }
//android_view_DisplayEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject messageQueueObj, jint vsyncSource) {
    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverWeak, messageQueue, vsyncSource);
    status_t status = receiver->initialize();
    return reinterpret_cast<jlong>(receiver.get());
}
//android_view_DisplayEventReceiver.cpp
class NativeDisplayEventReceiver : public DisplayEventDispatcher {}继承关系

NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource) :
        DisplayEventDispatcher(messageQueue->getLooper(),
                static_cast<ISurfaceComposer::VsyncSource>(vsyncSource)) {
}

note:

  1. DisplayEventReceiver.java会通过native调用最终去初始化native端的DisplayEventReceiver,需要注意mReceiverPtr,这个变量是nativeInit返回的,为jlong类型,实际上它是NativeDisplayEventReceiver对象的指针地址,当需要调用native方法的时候把这个long型传递给native,native就可以根据这个值找到对应的对象。后续还会遇到类似的设计,比如SurfaceControl的初始化,Surface的初始化等都会通过这样的方式来持有native端的对象
  2. NativeDisplayEventReceiver构造方法只是调用构造DisplayEventDispatcher,这里主要有两个动作,一是构造DisplayEventDispatcher,一是调用DisplayEventDispatcher的initialize()

构造DisplayEventDispatcher

//DisplayEventDispatcher.cpp
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
        ISurfaceComposer::VsyncSource vsyncSource) :
        mLooper(looper), mReceiver(vsyncSource), mWaitingForVsync(false) {
    ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}

mLooper就是java层一路传进来的
这里主要看看mReceiver(vsyncSource),这个vsyncSource就是前面传来的VSYNC_SOURCE_APP,mReceiver为DisplayEventReceiver类型变量。

//DisplayEventReceiver.cpp
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != nullptr) {
        mEventConnection = sf->createDisplayEventConnection(vsyncSource);
        if (mEventConnection != nullptr) {
            mDataChannel = std::make_unique<gui::BitTube>();
            mEventConnection->stealReceiveChannel(mDataChannel.get());
        }
    }
}

需要注意以下几点:

  1. 创建DisplayEventConnection,这里通过ISurfaceComposer来创建,而ComposerService实际上是对SurfaceFlinger的Binder代理的封装,所以这里实际上调用的是SF的方法createDisplayEventConnection,SF通过scheduler最终调用到EventThread的createEventConnection,关于SF的相关信息在讲解Surface的时候再详述
  2. BitTube:可以简单的理解为一个文件节点,把这个节点绑定给Looper之后,一端写入数据,另一端就可以读取,就像MessageQueue写入数据触发Looper读取是完全类似的。感兴趣的可以自行研究。
  3. stealReceiveChannel:把这个节点设置为通信通道
//EventThread.cpp
sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
    return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
}

这里构造EventThreadConnection,注意这里有个隐藏对象,那就是this,这个this代表的是EventThread,它的初始化是由SurfaceFlinger来完成的,这里先不研究。

//EventThread.cpp
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
                                             ResyncCallback resyncCallback)
      : resyncCallback(std::move(resyncCallback)),
        mEventThread(eventThread),
        mChannel(gui::BitTube::DefaultSize) {}

这个主要功能是把EventThread的引用扔给EventThreadConnection。
完成connection建立之后,实际上也是第一次引用该对象,所以会直接调用EventThreadConnection::onFirstRef()

//EventThread.cpp
void EventThreadConnection::onFirstRef() {
    // NOTE: mEventThread doesn't hold a strong reference on us
    mEventThread->registerDisplayEventConnection(this);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
    std::lock_guard<std::mutex> lock(mMutex);

    // this should never happen
    auto it = std::find(mDisplayEventConnections.cbegin(),
            mDisplayEventConnections.cend(), connection);
    if (it != mDisplayEventConnections.cend()) {
        ALOGW("DisplayEventConnection %p already exists", connection.get());
        mCondition.notify_all();
        return ALREADY_EXISTS;
    }

    mDisplayEventConnections.push_back(connection);
    mCondition.notify_all();
    return NO_ERROR;
}

检查新建立的connection是否已经存在,如果不存在则加入到mDisplayEventConnections中。

下面的类图展示了连接建立完成之后的各个类之间的关系:


EventThreadConnection.png

调用DisplayEventDispatcher的initialize()

//DisplayEventDispatcher.cpp
status_t DisplayEventDispatcher::initialize() {
    int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
            this, NULL);
}

其实就一个动作,把DisplayEventReceiver创建的BitTube节点添加给mLooper,这样mLooper就开始循环读取该节点的消息了,把this设置为该Looper的回调,也就是handleEvent,但有消息来的时候,就会回调handleEvent。

到这里connection连接就建立完成了,回调也配置好了,下面来一张图阐述这部分的流程,图中同时包含了EventThread的初始化:


EventThreadConnection_seq.png

第二部分 EventThread初始化

从SurfaceFlinger的init开始,SF作为系统服务,它是在系统启动的时候启动的,详细情况在介绍SF的时候再说。

//SurfaceFlinger.cpp
void SurfaceFlinger::init() {
    // start the EventThread
    mScheduler =
            getFactory().createScheduler([this](bool enabled) { setVsyncEnabled(enabled); },
                                         mRefreshRateConfigs);

    mAppConnectionHandle =
            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
                                         resyncCallback,
                                         impl::EventThread::InterceptVSyncsCallback());
    mSfConnectionHandle = mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
                                                       resyncCallback, [this](nsecs_t timestamp) {
                                                           mInterceptor->saveVSyncEvent(timestamp);
                                                       });
}

这里有两项工作,一是通过Factory构建Scheduler,二是通过Scheduler创建到EventThread的connection,分别创建了两条连接“app”和“sf”。

1. 构建Scheduler

//Scheduler.cpp
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
                     const scheduler::RefreshRateConfigs& refreshRateConfig) {
    // Note: We create a local temporary with the real DispSync implementation
    // type temporarily so we can initialize it with the configured values,
    // before storing it for more generic use using the interface type.
    auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
    primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
    mPrimaryDispSync = std::move(primaryDispSync);
}

两件事

  1. 定义mPrimaryDispSync指向DispSync,执行构造函数
  2. 调用init初始化DispSync
//DispSync.cpp
DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) {
    mThread = new DispSyncThread(name, mTraceDetailedInfo);
}
void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
    reset();
    beginResync();
}

可以看到这里主要就是创建了DispSyncThread,并启动开始循环,接着看看循环体:

//DispSync.cpp
    virtual bool threadLoop() {

        while (true) {
            std::vector<CallbackInvocation> callbackInvocations;
            { 
                targetTime = computeNextEventTimeLocked(now);
                bool isWakeup = false;
                if (now < targetTime) {
                    if (targetTime == INT64_MAX) {
                        ALOGV("[%s] Waiting forever", mName);
                        err = mCond.wait(mMutex);
                    } else {
                        ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime));
                        err = mCond.waitRelative(mMutex, targetTime - now);
                    }
                    if (err == TIMED_OUT) {
                        isWakeup = true;
                    } else if (err != NO_ERROR) {
                        ALOGE("error waiting for next event: %s (%d)", strerror(-err), err);
                        return false;
                    }
                }
                callbackInvocations = gatherCallbackInvocationsLocked(now);
            }
            if (callbackInvocations.size() > 0) {
                fireCallbackInvocations(callbackInvocations);
            }
        }
    }

三件事

  1. 计算Event触发时间
  2. 获取event监听对象
  3. 如果存在监听者,回调监听者注册的回调函数onDispSyncEvent,关于这个回调后面会有类图展示
    这里只需要知道周期性触发并回调即可。

2. 构建connection

//Scheduler.cpp
sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
        const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    const int64_t id = sNextId++;
    std::unique_ptr<EventThread> eventThread =
            makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
                            std::move(interceptCallback));

    auto eventThreadConnection =
            createConnectionInternal(eventThread.get(), std::move(resyncCallback));
    mConnections.emplace(id,
                         std::make_unique<Connection>(new ConnectionHandle(id),
                                                      eventThreadConnection,
                                                      std::move(eventThread)));
    return mConnections[id]->handle;
}
  1. sNextId : connection id,标志新建立的connection
  2. 构建EventThread
  3. 与EventThread建立连接
  4. 组建Connection,然后加入到mConnections,返回其对应的handle

构建EventThread

//Scheduler.cpp
std::unique_ptr<EventThread> Scheduler::makeEventThread(
        const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    std::unique_ptr<VSyncSource> eventThreadSource =
            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
    return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
                                               std::move(interceptCallback), connectionName);
}

创建DispSyncSource,它将作为EventThread与DispSync通信的桥梁
创建EventThread

//EventThread.cpp
EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
      : mVSyncSource(src),
        mVSyncSourceUnique(std::move(uniqueSrc)),
        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
        mThreadName(threadName) {
    mVSyncSource->setCallback(this);

    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);
        threadMain(lock);
    });
}

三件事

  1. 赋值mVSyncSource=DispSyncSource,并设置callback为this,EventThread继承了VSyncSource::Callback,所以DispSyncSource回调将会调用到onVSyncEvent
  2. 设置name为“app”
  3. 创建thread并在thread中运行threadMain,重点关注threadMain


    EventThread_class.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352