• Android GUI系统之SurfaceFlinger(16)MessageBase解读


    该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录

    说明:

    • 关于导读:导读部分主要是方便初学者理解SurfaceFlinger代码中的机制,为后面分析代码打下一个更好的基础,这样就可以把更多的精力放在surfaceFlinger的业务逻辑分析上。
    • 关于代码分支:以下代码分析均在android5.1.1_r3分支上 目录frameworks/native/services/surfaceflinger为root目录

    1 MessageBase解读

    1.1 源码实现分析

    MessageBase源码实现如下:

    1. //头文件部分MessageQueue.h
    2. class MessageBase : public MessageHandler
    3. {
    4. public:
    5. MessageBase();
    6. virtual bool handler() = 0;
    7. void wait() const { barrier.wait(); } //等待handle处理消息
    8. protected:
    9. virtual ~MessageBase();
    10. private:
    11. virtual void handleMessage(const Message& message);
    12. mutable Barrier barrier;
    13. };
    14. //实现部分MessageQueue.cpp
    15. MessageBase::MessageBase(): MessageHandler() {}
    16. MessageBase::~MessageBase() {}
    17. void MessageBase::handleMessage(const Message&) {
    18. this->handler();
    19. barrier.open();//处理完消息,打开屏障
    20. };

    1.2 理解Barrier机制

    C++中的Barrier机制形象解读如下:

    当涉及到多线程的同步问题时,这里用一个小故事来形象解读Barrier的原理。假设有一群小朋友在进行一场集体活动,他们需要在一个关键点上同步行动。这个关键点是一个大门,只有当所有小朋友都到达大门时,才能一起进入下一个阶段的活动。

    • 开始时,所有的小朋友都在不同的地方,他们要在指定的时间内集合到大门前。每个小朋友都代表一个线程,他们需要同时到达大门,才能继续进行下一步的活动。
    • 当活动开始时,大门是关闭的,表示屏障状态为关闭(CLOSED)。小朋友们在各自的位置启动,开始向大门前进。每个小朋友到达大门时,他们会等待其他小朋友到达。
    • 当最后一个小朋友到达大门时,屏障状态变为开放(OPENED)。大门打开了,所有小朋友都能看到大门已经开了,他们同时继续通过大门进入下一个阶段的活动。
    • 这个故事中的大门就是 Barrier,它提供了一个同步点,确保所有小朋友(线程)都到达了同一个位置后才能继续进行下一步的活动。当屏障状态为关闭时,小朋友们会等待,直到最后一个小朋友到达,屏障状态变为开放,所有小朋友都能继续执行后续的操作。

    通过这个故事,可以更形象地理解 Barrier 的原理,它在多线程环境中起到类似于大门的作用,让线程们在同一个关键点上同步等待,以保证并发操作的正确执行和数据的一致性。
    android源码中使用lock和condition机制 构建了一个Barrier的机制,实现如下:

    1. #include
    2. #include
    3. #include
    4. namespace android {
    5. class Barrier
    6. {
    7. public:
    8. inline Barrier() : state(CLOSED) { }
    9. inline ~Barrier() { }
    10. //释放处于等待状态的线程,将屏障状态设置为开放(OPENED),并唤醒所有等待的线程。
    11. void open() {
    12. Mutex::Autolock _l(lock);
    13. state = OPENED;
    14. cv.broadcast();
    15. }
    16. //重置屏障,将状态设置为关闭(CLOSED),以便 wait() 方法可以将线程阻塞。
    17. void close() {
    18. Mutex::Autolock _l(lock);
    19. state = CLOSED;
    20. }
    21. //等待屏障状态变为开放(OPENED)。如果屏障状态为关闭(CLOSED),则线程将在此处阻塞等待,直到 open() 方法被调用。
    22. void wait() const {
    23. Mutex::Autolock _l(lock);
    24. while (state == CLOSED) {
    25. cv.wait(lock);
    26. }
    27. }
    28. private:
    29. enum { OPENED, CLOSED };
    30. mutable Mutex lock;
    31. mutable Condition cv;
    32. volatile int state;
    33. };
    34. }; // namespace android
    35. #endif // ANDROID_BARRIER_H

    根据以上实现,在android的surfaceFlinger中,并没有使用barrier的close操作。那么这是为什么呢?

    • 因为它的设计并不需要在每次使用之前显式地进行关闭。barrier在SurfaceFlinger中被用作线程之间的同步机制,用于控制消息的处理和执行顺序。它的目的是等待其他线程完成一定操作后再继续执行,而不是重复使用。在SurfaceFlinger的消息处理中,barrier被用作同步点,以确保特定操作在特定时机执行。一旦消息处理线程到达barrier,它会等待其他线程(通常是主线程或 GPU 线程)完成特定的操作,然后继续执行。
    • 由于barrier的使用场景是等待其他线程完成操作,而不是重复使用,因此在SurfaceFlinger中的MessageBase中没有使用close()操作进行显式关闭。在每个消息的处理过程中,barrier只需等待其他线程完成操作即可,而无需重置为初始状态。

    1.3 SurfaceFlinger中同步/异步消息实现

    在MessageBase的代码中handleMessage中每次处理完消息后会执行barrier.open();来打开屏障,而在SurfaceFlinger中把消息放入消息队列时候,如果采用同步操作,等待上一个消息处理完毕,这里的msg的类型就是继承了MessageBase。这里的wait就是MessageBase中的wait实现,调用的就是barrier的wait操作,这样,当上一个消息处理完执行了barrier的open操作,wait操作才会解除阻塞。同步消息方法postMessageSync详细代码如下:

    1. status_t SurfaceFlinger::postMessageSync(const sp& msg,
    2. nsecs_t reltime, uint32_t /* flags */) {
    3. status_t res = mEventQueue.postMessage(msg, reltime);
    4. if (res == NO_ERROR) {
    5. msg->wait();
    6. }
    7. return res;
    8. }

    而对于异步消息操作,则不需要wait操作,因而实现反而简单的多,不需要等待处理结果。异步消息方法postMessageAsync详细代码如下所示:

    1. status_t SurfaceFlinger::postMessageAsync(const sp& msg,
    2. nsecs_t reltime, uint32_t /* flags */) {
    3. return mEventQueue.postMessage(msg, reltime);
    4. }

    2 MessageBase在SurfaceFlinger中的用法

    2.1 原理说明

    这里要注意的是MessageBase是继承MessageHandler的,这意味着执行SurfaceFlinger中的postMessageSync方法时,会将对应的msg放入消息队列当中,等待SurfaceFlinger处理消息并发送回应信号。这里处理消息会直接调用对应MessageBase对象的handleMessage方法,进而执行对应MessageBase对象的handler()方法。

    2.2 使用案例参考

    上面对MessageBase的机制有了一个具体的了解,接下来看看SurfaceFlinger中一般是怎么使用这个机制的。

    2.2.1 Client::createSurface方法实现参考

    createSurface 方法主要用于创建surface,具体功能如下:

    • 创建Surface对象:在客户端应用程序中调用createSurface方法时,SurfaceFlinger会创建一个Surface对象,用于表示应用程序中的窗口或图形内容。
    • 配置Surface属性:createSurface方法接受一组参数,用于配置Surface的属性,如宽度、高度、格式等。这些属性决定了Surface的显示特性和渲染方式。
    • 添加到图层层级结构:SurfaceFlinger会将创建的Surface对象添加到图层层级结构中的适当位置。这样,SurfaceFlinger就能够管理多个Surface,并控制它们的显示顺序、位置、透明度等。

    这里代码参考了Client.cpp中createSurface方法的实现,如下所示:

    1. status_t Client::createSurface(
    2. const String8& name,
    3. uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
    4. sp* handle,
    5. sp* gbp)
    6. {
    7. class MessageCreateLayer : public MessageBase {
    8. SurfaceFlinger* flinger;
    9. Client* client;
    10. sp* handle;
    11. sp* gbp;
    12. status_t result;
    13. const String8& name;
    14. uint32_t w, h;
    15. PixelFormat format;
    16. uint32_t flags;
    17. public:
    18. //构造函数实现
    19. MessageCreateLayer(SurfaceFlinger* flinger,const String8& name, Client* client,
    20. uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
    21. sp* handle,sp* gbp)
    22. : flinger(flinger), client(client),handle(handle), gbp(gbp),
    23. name(name), w(w), h(h), format(format), flags(flags) {
    24. }
    25. status_t getResult() const { return result; }
    26. //消息处理实现,handle message机制会调用handleMessage方法,进而会调用到handler方法
    27. virtual bool handler() {
    28. //这里的flinger指的就是SurfaceFlinger实例,通过构造函数传入
    29. result = flinger->createLayer(name, client, w, h, format, flags,handle, gbp);
    30. return true;
    31. }
    32. };
    33. //
    34. sp msg = new MessageCreateLayer(mFlinger.get(),
    35. name, this, w, h, format, flags, handle, gbp);
    36. mFlinger->postMessageSync(msg);
    37. return static_cast( msg.get() )->getResult();
    38. }

    这里的使用流程简要成两步,说明如下:

    1. 构建一个MessageBase子类MessageCreateLayer,实现MessageCreateLayer的构造方法和handler方法
    2. 创建MessageCreateLayer对象并将msg消息加入消息队列等待处理,msg对应的处理方法为handler。

    这里思考一个问题:可以看到Client::createSurface最终是调用了SurfaceFlinger::createLayer方法。那么为什么不直接调用呢?

    在给出的代码中,Client::createSurface 方法通过创建一个名为 MessageCreateLayer 的自定义消息类来间接调用 SurfaceFlinger::createLayer 方法,而不是直接调用。这种间接调用的方式有几个目的和好处:

    • 解耦和模块化:通过使用消息的方式进行通信,Client 类和 SurfaceFlinger 类之间的耦合性降低。Client 类只需要创建消息并将其发送给 SurfaceFlinger,而不需要直接依赖于 SurfaceFlinger 的具体实现。这样可以提高代码的灵活性和可维护性。
    • 异步处理:通过使用消息队列和异步处理机制,Client 类可以继续执行后续的操作,而不需要等待 SurfaceFlinger::createLayer 方法的完成。这样可以避免因某个操作阻塞而导致整个应用程序的响应性下降。
    • 线程安全:由于消息队列通常是在多线程环境下使用的,通过将操作封装为消息并由消息处理循环执行,可以确保对 SurfaceFlinger 的访问是线程安全的。这可以避免多个线程同时访问 SurfaceFlinger 导致的竞态条件和数据不一致性。

    总结起来,通过间接调用的方式,即通过创建自定义消息并发送给 SurfaceFlinger 的消息队列,Client::createSurface 方法可以实现解耦和模块化、异步处理和线程安全等优势。这样的设计可以提高代码的可维护性和系统的性能。

    2.2.2 setActiveConfig方法实现参考

    setActiveConfig方法主要用于设置当前显示设备的活动配置。具体功能如下:

    • 切换配置:通过调用该方法,SurfaceFlinger可以切换到指定的配置,将显示设备的输出调整为该配置所定义的特性。这可能包括更改分辨率、刷新率或其他显示参数。
    • 确保兼容性:在切换到新的活动配置之前,setActiveConfig方法会进行一些兼容性检查。例如,它可能会检查新配置是否受支持,是否与其他正在显示的图层兼容等。这有助于确保切换到新配置不会导致显示问题或不稳定的情况。
    • 屏幕重启:在某些情况下,切换活动配置可能需要重新初始化显示设备,这可能会导致屏幕短暂黑屏或重启。setActiveConfig方法会涉及与硬件或底层系统交互,以便正确处理这些情况。

    这里代码参考了SurfaceFlinger.cpp中setActiveConfig方法的实现,如下所示:

    1. status_t SurfaceFlinger::setActiveConfig(const sp& display, int mode) {
    2. class MessageSetActiveConfig: public MessageBase {
    3. SurfaceFlinger& mFlinger;
    4. sp mDisplay;
    5. int mMode;
    6. public:
    7. MessageSetActiveConfig(SurfaceFlinger& flinger, const sp& disp,
    8. int mode) :
    9. mFlinger(flinger), mDisplay(disp) { mMode = mode; }
    10. virtual bool handler() {
    11. Vector configs;
    12. mFlinger.getDisplayConfigs(mDisplay, &configs);
    13. if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
    14. ALOGE("Attempt to set active config = %d for display with %zu configs",
    15. mMode, configs.size());
    16. }
    17. sp hw(mFlinger.getDisplayDevice(mDisplay));
    18. if (hw == NULL) {
    19. ALOGE("Attempt to set active config = %d for null display %p",
    20. mMode, mDisplay.get());
    21. } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
    22. ALOGW("Attempt to set active config = %d for virtual display",
    23. mMode);
    24. } else {
    25. mFlinger.setActiveConfigInternal(hw, mMode);
    26. }
    27. return true;
    28. }
    29. };
    30. sp msg = new MessageSetActiveConfig(*this, display, mode);
    31. postMessageSync(msg);
    32. return NO_ERROR;
    33. }

    这里的使用流程简要成两步,说明如下:

    1. 构建一个MessageBase子类MessageSetActiveConfig,实现MessageSetActiveConfig的构造方法和handler方法,与2.2.1中的createSurface相比handler逻辑相对复杂。
    2. 创建MessageSetActiveConfig对象并将msg消息加入消息队列等待处理,msg对应的处理方法为handler。

    当然,在SurfaceFlinger中还有其他的使用案例,大同小异,这里就不再赘述了。

  • 相关阅读:
    Qt开发-QT Widgets
    STA series --- 8.Timing Verification (PARTI)
    LeetCode热题100——二分查找
    【网页设计】期末大作业html+css(体育网站--足球 6页 )
    Mac环境部署单机版Hbase及使用JavaAPI对Hbase增删改查
    【Kafka】单分区单副本增加至多分区多副本
    CSRF跨站请求伪造:django中如何攻击与防御
    第十八章:Swing自述
    嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型
    文件系统(七):文件系统崩溃一致性、方法、原理与局限
  • 原文地址:https://blog.csdn.net/vviccc/article/details/132769977