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

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

说明:以下代码分析均在android5.1.1_r3分支上 目录frameworks/native/services/surfaceflinger为root目录

1 MessageBase解读

1.1 源码实现分析

MessageBase源码实现如下:

//头文件部分MessageQueue.h
class MessageBase : public MessageHandler
{
public:
    MessageBase();
    virtual bool handler() = 0;
    void wait() const { barrier.wait(); } //等待handle处理消息
protected:
    virtual ~MessageBase();
private:
    virtual void handleMessage(const Message& message);
    mutable Barrier barrier;
};

//实现部分MessageQueue.cpp
MessageBase::MessageBase(): MessageHandler() {}
MessageBase::~MessageBase() {}
void MessageBase::handleMessage(const Message&) {
    this->handler();
    barrier.open();//处理完消息,打开屏障
};

1.2 理解Barrier机制

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

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

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

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

#include 
#include 
#include 

namespace android {
class Barrier
{
public:
    inline Barrier() : state(CLOSED) { }
    inline ~Barrier() { }

    //释放处于等待状态的线程,将屏障状态设置为开放(OPENED),并唤醒所有等待的线程。
    void open() {
        Mutex::Autolock _l(lock);
        state = OPENED;
        cv.broadcast();
    }

    //重置屏障,将状态设置为关闭(CLOSED),以便 wait() 方法可以将线程阻塞。
    void close() {
        Mutex::Autolock _l(lock);
        state = CLOSED;
    }

    //等待屏障状态变为开放(OPENED)。如果屏障状态为关闭(CLOSED),则线程将在此处阻塞等待,直到 open() 方法被调用。
    void wait() const {
        Mutex::Autolock _l(lock);
        while (state == CLOSED) {
            cv.wait(lock);
        }
    }
private:
    enum { OPENED, CLOSED };
    mutable     Mutex       lock;
    mutable     Condition   cv;
    volatile    int         state;
};
}; // namespace android

#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详细代码如下:

status_t SurfaceFlinger::postMessageSync(const sp& msg,
        nsecs_t reltime, uint32_t /* flags */) {
    status_t res = mEventQueue.postMessage(msg, reltime);
    if (res == NO_ERROR) {
        msg->wait();
    }
    return res;
}

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

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

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方法的实现,如下所示:

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp* handle,
        sp* gbp)
{
    class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
        sp* handle;
        sp* gbp;
        status_t result;
        const String8& name;
        uint32_t w, h;
        PixelFormat format;
        uint32_t flags;
    public:
        //构造函数实现
        MessageCreateLayer(SurfaceFlinger* flinger,const String8& name, Client* client,
                uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
                sp* handle,sp* gbp)
            : flinger(flinger), client(client),handle(handle), gbp(gbp),
              name(name), w(w), h(h), format(format), flags(flags) {
        }
        status_t getResult() const { return result; }
        //消息处理实现,handle message机制会调用handleMessage方法,进而会调用到handler方法
        virtual bool handler() {
            //这里的flinger指的就是SurfaceFlinger实例,通过构造函数传入
            result = flinger->createLayer(name, client, w, h, format, flags,handle, gbp);
            return true;
        }
    };
    //
    sp msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle, gbp);
    mFlinger->postMessageSync(msg);
    return static_cast( msg.get() )->getResult();
}

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

  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方法的实现,如下所示:

status_t SurfaceFlinger::setActiveConfig(const sp& display, int mode) {
    class MessageSetActiveConfig: public MessageBase {
        SurfaceFlinger& mFlinger;
        sp mDisplay;
        int mMode;
    public:
        MessageSetActiveConfig(SurfaceFlinger& flinger, const sp& disp,
                               int mode) :
            mFlinger(flinger), mDisplay(disp) { mMode = mode; }
        virtual bool handler() {
            Vector configs;
            mFlinger.getDisplayConfigs(mDisplay, &configs);
            if (mMode < 0 || mMode >= static_cast(configs.size())) {
                ALOGE("Attempt to set active config = %d for display with %zu configs",
                        mMode, configs.size());
            }
            sp hw(mFlinger.getDisplayDevice(mDisplay));
            if (hw == NULL) {
                ALOGE("Attempt to set active config = %d for null display %p",
                        mMode, mDisplay.get());
            } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
                ALOGW("Attempt to set active config = %d for virtual display",
                        mMode);
            } else {
                mFlinger.setActiveConfigInternal(hw, mMode);
            }
            return true;
        }
    };
    sp msg = new MessageSetActiveConfig(*this, display, mode);
    postMessageSync(msg);
    return NO_ERROR;
}

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

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

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

你可能感兴趣的:(framework,android,显示子系统,android,c++,framework)