该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录
说明:
- 关于导读:导读部分主要是方便初学者理解SurfaceFlinger代码中的机制,为后面分析代码打下一个更好的基础,这样就可以把更多的精力放在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 <stdint.h>
#include <sys/types.h>
#include <utils/threads.h>
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<MessageBase>& 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<MessageBase>& 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<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp)
{
class MessageCreateLayer : public MessageBase {
SurfaceFlinger* flinger;
Client* client;
sp<IBinder>* handle;
sp<IGraphicBufferProducer>* 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<IBinder>* handle,sp<IGraphicBufferProducer>* 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<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
name, this, w, h, format, flags, handle, gbp);
mFlinger->postMessageSync(msg);
return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}
这里的使用流程简要成两步,说明如下:
- 构建一个MessageBase子类MessageCreateLayer,实现MessageCreateLayer的构造方法和handler方法
- 创建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<IBinder>& display, int mode) {
class MessageSetActiveConfig: public MessageBase {
SurfaceFlinger& mFlinger;
sp<IBinder> mDisplay;
int mMode;
public:
MessageSetActiveConfig(SurfaceFlinger& flinger, const sp<IBinder>& disp,
int mode) :
mFlinger(flinger), mDisplay(disp) { mMode = mode; }
virtual bool handler() {
Vector<DisplayInfo> configs;
mFlinger.getDisplayConfigs(mDisplay, &configs);
if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
ALOGE("Attempt to set active config = %d for display with %zu configs",
mMode, configs.size());
}
sp<DisplayDevice> 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<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode);
postMessageSync(msg);
return NO_ERROR;
}
这里的使用流程简要成两步,说明如下:文章来源:https://www.toymoban.com/news/detail-707710.html
- 构建一个MessageBase子类MessageSetActiveConfig,实现MessageSetActiveConfig的构造方法和handler方法,与2.2.1中的createSurface相比handler逻辑相对复杂。
- 创建MessageSetActiveConfig对象并将msg消息加入消息队列等待处理,msg对应的处理方法为handler。
当然,在SurfaceFlinger中还有其他的使用案例,大同小异,这里就不再赘述了。文章来源地址https://www.toymoban.com/news/detail-707710.html
到了这里,关于Android GUI系统之SurfaceFlinger(16)MessageBase解读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!