背景
熟悉Android的同学对Handler、Looper、MessageQueue、Message应该是非常熟悉了,Android是一个基于消息驱动的系统,我们在日常开发中用到消息队里的地方非常多。Android也给我们封装好了一个强大易用的消息处理API,但是我们怎么在C++层实现这一功能了。
不需要多复杂的IPC机制,我们只需要实现一个简易的消息队列机制就行了。Android的消息队列机制实现太过复杂了,其实在音视频中很多东西不需要这么复杂,我们只需要将我们需要的那部分抠出来就可以了。
Android消息队列
Android中消息分发相关的类有:
- HandlerThread
- Looper
- Handler
- MessageQueue
- Message
正常构建一个消息分发机制的代码如下:
HandlerThread thread = new HandlerThread("Message Thread");
thread.start();
Handler handler = new Handler(thread.getLooper());
//......
handler.sendMessage(....)
大体的流程如下:
- 通过创建HandlerThread实例,HandlerThread实例中构建一个Looper实例
- 通过调用HandlerThread实例的start()方法开始执行消息队列轮转,进入Looper中的轮转
- Handler实例中持有刚刚创建的Looper实例
- Looper实例中构建一个消息队列MessageQueue
- Handler每次发送消息都会通过Handler持有的Looper实例添加到消息队列中
- Looper轮转中会消化处理消息
简单的流程示意如下图:
可以看到Looper.java中的轮转函数中有无限循环在执行,这个无限循环中会不断地消费消息队列中的消息(如果消息队列中存在消息的话),如果消息队列中不存在消息,那就一直等着。
从上面我们简单地分析中可以比较清晰地了解了Android原生的消息队列机制,不过有些地方实现的过于复杂了,在音视频SDK处理中可以不必要这么复杂,至于复杂的地方我在下面会提到的。下面我们根据对Android原生消息队列的分析来提供C++层的消息队列机制。
C++消息队列
我们照葫芦画瓢在C++中定义了几个文件:
- handler_thread.cc
- handler.cc
- looper.cc
- message_queue.cc
- message.cc
每个文件提供的功能和Android基本上一直,不过我们还是先简单分析一下代码,方便后续的阐述。
1.handler_thread
handler_thread.h
#include <pthread.h>
#include <string>
#include "handler.h"
#include "looper.h"
namespace thread {
class HandlerThread {
public:
static HandlerThread *Create(std::string name);
void RunInternal();
private:
HandlerThread(std::string name);
public:
~HandlerThread();
void Quit();
bool QuitSafely();
Looper *GetLooper();
private:
std::string name_;
pthread_t thread_;
pthread_mutex_t mutex_;
pthread_cond_t cond_;
Looper *looper_;
bool exiting_;
bool exited_;
};
} // namespace thread
handler_thread.cc
#include "handler_thread.h"
#include "log.h"
namespace thread {
HandlerThread *HandlerThread::Create(std::string name) {
return new HandlerThread(name);
}
static void *RunTask(void *context) {
auto handler_thread = reinterpret_cast<HandlerThread *>(context);
handler_thread->RunInternal();
pthread_exit(nullptr);
}
HandlerThread::HandlerThread(std::string name)
: name_(name)
, looper_(nullptr)
, exiting_(false)
, exited_(false) {
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&thread_, &attr, RunTask, (void *) this);
}
void HandlerThread::RunInternal() {
pthread_mutex_lock(&mutex_);
exiting_ = false;
exited_ = false;
pthread_mutex_unlock(&mutex_);
Looper::Prepare();
pthread_mutex_lock(&mutex_);
looper_ = Looper::MyLooper();
pthread_cond_broadcast(&cond_);
pthread_mutex_unlock(&mutex_);
Looper::Loop();
Looper::Exit();
pthread_mutex_lock(&mutex_);
exiting_ = false;
looper_ = nullptr;
exited_ = true;
pthread_mutex_unlock(&mutex_);
}
HandlerThread::~HandlerThread() {
if (looper_) {
looper_->Quit(true);
}
pthread_join(thread_, nullptr);
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
if (looper_) {
delete looper_;
looper_ = nullptr;
}
}
void HandlerThread::Quit() {
pthread_mutex_lock(&mutex_);
if (exiting_ || exited_) {
pthread_mutex_unlock(&mutex_);
return;
}
exiting_ = true;
pthread_mutex_unlock(&mutex_);
Looper *looper = GetLooper();
if (looper) {
looper->Quit(false);
}
}
bool HandlerThread::QuitSafely() {
pthread_mutex_lock(&mutex_);
if (exiting_ || exited_) {
pthread_mutex_unlock(&mutex_);
return false;
}
exiting_ = true;
pthread_mutex_unlock(&mutex_);
Looper *looper = GetLooper();
if (looper) {
looper->Quit(true);
return true;
}
return false;
}
Looper *HandlerThread::GetLooper() {
pthread_mutex_lock(&mutex_);
if (exited_) {
LOGE("Thread has been exited");
pthread_mutex_unlock(&mutex_);
return nullptr;
}
if (looper_ == nullptr) {
LOGE("Thread should wait");
pthread_cond_wait(&cond_, &mutex_);
}
pthread_mutex_unlock(&mutex_);
return looper_;
}
2.handler
handler.h
#include "looper.h"
#include "message.h"
namespace thread {
class Looper;
class Message;
/**
* 消息处理回调
*/
class HandlerCallback {
public:
virtual void HandleMessage(Message *msg) {}
};
class Handler {
public:
Handler(Looper *looper, HandlerCallback *callback);
~Handler();
/**
* 发送消息
* @param msg
*/
void SendMessage(Message *msg);
/**
* looper中调用此方法回调
* @param msg
*/
void DispatchMessage(Message *msg);
/**
* 销毁消息队列中msg.what = what的消息
* @param what
*/
void RemoveMessage(int what);
/**
* 消息队列中消息数
* @return
*/
int Size();
private:
Looper *looper_;
HandlerCallback *callback_;
};
} // namespace thread
handler.cc
#include "handler.h"
#include "log.h"
/**
*
* HandlerThread 持有 Looper
* Handler 持有 Looper
* Handler 发送消息通过Looper轮转消息
* Looper 中持有MessageQueue来管理消息
*/
namespace thread {
Handler::Handler(Looper *looper, HandlerCallback *callback)
: looper_(looper)
, callback_(callback) {
}
Handler::~Handler() {
}
void Handler::SendMessage(Message *msg) {
if (looper_) {
msg->target = this;
looper_->SendMessage(msg);
}
}
void Handler::DispatchMessage(Message *msg) {
if (callback_) {
callback_->HandleMessage(msg);
}
}
void Handler::RemoveMessage(int what) {
if (looper_) {
looper_->RemoveMessage(what);
}
}
int Handler::Size() {
if (looper_) {
return looper_->Size();
}
return 0;
}
}
3.looper
looper.h
#include "message.h"
#include "message_queue.h"
#include <cstdlib>
#include <map>
#include <mutex>
namespace thread {
class Message;
class MessageQueue;
class Looper {
public:
Looper();
~Looper();
/**
* 创建消息looper实例
*/
static void Prepare();
/**
* 开始执行消息循环
*/
static void Loop();
static Looper *MyLooper();
static int64_t MyLooperId();
static void Exit();
/**
* 如果safely = true, 表示要将消息队列中消息分发完了才行
* safely = false, 忽略消息队列中剩余消息, 直接退出
* @param safely
*/
void Quit(bool safely);
void Dump();
/**
* 消息队列中消息数
* @return
*/
int Size();
void SendMessage(Message *msg);
void RemoveMessage(int what);
private:
/**
* 消息循环处理函数
*/
void LoopInternal();
void EnqueueMessage(Message *msg);
Message *Take();
private:
bool exiting_;
bool exited_;
bool exit_safely_;
bool looping_;
pthread_mutex_t variable_mutex_;
MessageQueue *message_queue_;
};
class LooperManager {
public:
friend Looper;
static LooperManager *GetInstance();
public:
LooperManager();
~LooperManager();
Looper *Create(int64_t tid);
Looper *Get(int64_t tid);
void Remove(int64_t tid);
int Size();
private:
static LooperManager *instance_;
std::map<int64_t, Looper *> looper_map_;
std::mutex looper_mutex_;
};
} // namespace thread
looper.cc
#include "looper.h"
#include "thread.h"
#include "log.h"
#include <cassert>
#include "time_utils.h"
namespace thread {
Looper::Looper()
: exiting_(false)
, exited_(false)
, exit_safely_(false)
, looping_(false) {
message_queue_ = new MessageQueue();
pthread_mutex_init(&variable_mutex_, nullptr);
}
Looper::~Looper() {
pthread_mutex_destroy(&variable_mutex_);
}
void Looper::Prepare() {
int64_t tid = Thread::CurrentThreadId();
Looper *looper = LooperManager::GetInstance()->Create(tid);
if (looper == nullptr) {
LOGE("Current thread looper has been called");
}
}
void Looper::Loop() {
MyLooper()->LoopInternal();
}
Looper * Looper::MyLooper() {
int64_t tid = Thread::CurrentThreadId();
Looper *looper = LooperManager::GetInstance()->Get(tid);
if (looper == nullptr) {
LOGE("Please invoke Looper::Prepare first");
}
assert(looper);
return looper;
}
int64_t Looper::MyLooperId() {
return reinterpret_cast<int64_t>(MyLooper());
}
void Looper::Exit() {
int64_t tid = Thread::CurrentThreadId();
LooperManager::GetInstance()->Remove(tid);
}
void Looper::Quit(bool safely) {
pthread_mutex_lock(&variable_mutex_);
if (exiting_ || exited_) {
pthread_mutex_unlock(&variable_mutex_);
return;
}
exit_safely_ = safely;
exiting_ = true;
pthread_mutex_unlock(&variable_mutex_);
message_queue_->Notify();
}
void Looper::Dump() {
message_queue_->Dump();
}
int Looper::Size() {
return message_queue_->Size();
}
void Looper::SendMessage(Message *msg) {
pthread_mutex_lock(&variable_mutex_);
if (exiting_ || exited_) {
pthread_mutex_unlock(&variable_mutex_);
return;
}
pthread_mutex_unlock(&variable_mutex_);
EnqueueMessage(msg);
}
void Looper::RemoveMessage(int what) {
message_queue_->RemoveMessage(what);
}
void Looper::LoopInternal() {
pthread_mutex_lock(&variable_mutex_);
if (looping_ || exiting_ || exited_) {
pthread_mutex_unlock(&variable_mutex_);
return;
}
looping_ = true;
pthread_mutex_unlock(&variable_mutex_);
for (;;) {
Message *msg = Take();
if (msg) {
if (msg->target) {
msg->target->DispatchMessage(msg);
}
delete msg;
}
pthread_mutex_lock(&variable_mutex_);
if (exit_safely_) {
if (exiting_ && message_queue_->Size() == 0) {
pthread_mutex_unlock(&variable_mutex_);
break;
}
} else {
if (exiting_) {
pthread_mutex_unlock(&variable_mutex_);
break;
}
}
pthread_mutex_unlock(&variable_mutex_);
}
int64_t time = TimeUtils::GetCurrentTimeUs();
while (message_queue_->Size() > 0) {
Message *msg = message_queue_->Take();
if (msg) {
delete msg;
}
}
message_queue_->Clear();
LOGI("Clear message_queue cost time=%lld us", (TimeUtils::GetCurrentTimeUs() - time));
pthread_mutex_lock(&variable_mutex_);
exiting_ = false;
exited_ = true;
looping_ = false;
pthread_mutex_unlock(&variable_mutex_);
}
void Looper::EnqueueMessage(Message *msg) {
/// TODO msg 模式, 可以放在队头, 也可以放在队尾
message_queue_->Offer(msg);
}
Message * Looper::Take() {
return message_queue_->Take();
}
/// ------------------------------------------------------------------
LooperManager *LooperManager::instance_ = new LooperManager();
LooperManager::LooperManager() {
}
LooperManager::~LooperManager() {
}
LooperManager * LooperManager::GetInstance() {
return instance_;
}
Looper * LooperManager::Create(int64_t tid) {
std::lock_guard<std::mutex> guard(looper_mutex_);
auto it = looper_map_.find(tid);
if (it == looper_map_.end()) {
Looper *looper = new Looper();
looper_map_[tid] = looper;
return looper;
}
return nullptr;
}
Looper * LooperManager::Get(int64_t tid) {
std::lock_guard<std::mutex> guard(looper_mutex_);
auto it = looper_map_.find(tid);
if (it == looper_map_.end()) {
return nullptr;
}
return it->second;
}
void LooperManager::Remove(int64_t tid) {
std::lock_guard<std::mutex> guard(looper_mutex_);
auto it = looper_map_.find(tid);
if (it != looper_map_.end()) {
looper_map_.erase(it);
}
}
int LooperManager::Size() {
std::lock_guard<std::mutex> guard(looper_mutex_);
return looper_map_.size();
}
}
4.message_queue
message_queue.h
#include "message.h"
#include <pthread.h>
#include <list>
namespace thread {
class Message;
class MessageQueue {
public:
MessageQueue();
~MessageQueue();
/**
* 发送的消息放在消息队列结尾
* @param msg
*/
void Offer(Message *msg);
/**
* 发送的消息放在消息队列开头
* @param msg
*/
void OfferAtFront(Message *msg);
/**
* 获取消息
* @return
*/
Message *Take();
/**
* 解锁
*/
void Notify();
int Size();
bool IsEmpty();
void Clear();
/**
* 删除msg.what = what的消息
* @param what
*/
void RemoveMessage(int what);
void Dump();
private:
pthread_mutex_t queue_mutex_;
pthread_cond_t queue_cond_;
/// 存放消息的队列
std::list<Message *> queue_;
bool is_destroyed_;
};
} // namespace thread
message_queue.cc
#include "message_queue.h"
#include "log.h"
#include <sstream>
namespace thread {
MessageQueue::MessageQueue()
: is_destroyed_(false) {
pthread_mutex_init(&queue_mutex_, nullptr);
pthread_cond_init(&queue_cond_, nullptr);
}
MessageQueue::~MessageQueue() {
LOGI("Enter");
pthread_mutex_lock(&queue_mutex_);
is_destroyed_ = true;
pthread_mutex_unlock(&queue_mutex_);
Clear();
pthread_mutex_destroy(&queue_mutex_);
pthread_cond_destroy(&queue_cond_);
LOGI("Leave");
}
void MessageQueue::Offer(Message *msg) {
pthread_mutex_lock(&queue_mutex_);
if (is_destroyed_) {
pthread_mutex_unlock(&queue_mutex_);
return;
}
queue_.push_back(msg);
pthread_cond_broadcast(&queue_cond_);
pthread_mutex_unlock(&queue_mutex_);
}
void MessageQueue::OfferAtFront(Message *msg) {
pthread_mutex_lock(&queue_mutex_);
if (is_destroyed_) {
pthread_mutex_unlock(&queue_mutex_);
return;
}
queue_.push_front(msg);
pthread_cond_broadcast(&queue_cond_);
pthread_mutex_unlock(&queue_mutex_);
}
Message *MessageQueue::Take() {
pthread_mutex_lock(&queue_mutex_);
if (is_destroyed_) {
pthread_mutex_unlock(&queue_mutex_);
return nullptr;
}
if (Size() <= 0) {
pthread_cond_wait(&queue_cond_, &queue_mutex_);
}
if (queue_.empty()) {
pthread_mutex_unlock(&queue_mutex_);
return nullptr;
}
Message *msg = queue_.front();
queue_.pop_front();
pthread_mutex_unlock(&queue_mutex_);
return msg;
}
void MessageQueue::Notify() {
pthread_mutex_lock(&queue_mutex_);
pthread_cond_broadcast(&queue_cond_);
pthread_mutex_unlock(&queue_mutex_);
}
int MessageQueue::Size() {
return queue_.size();
}
bool MessageQueue::IsEmpty() {
return queue_.empty();
}
void MessageQueue::Clear() {
Notify();
if (queue_.empty()) {
return;
}
pthread_mutex_lock(&queue_mutex_);
while (!queue_.empty()) {
Message *msg = queue_.front();
queue_.pop_front();
if (msg) {
delete msg;
}
}
queue_.clear();
pthread_mutex_unlock(&queue_mutex_);
}
void MessageQueue::RemoveMessage(int what) {
pthread_mutex_lock(&queue_mutex_);
if (is_destroyed_) {
pthread_mutex_unlock(&queue_mutex_);
return;
}
std::list<Message *>::iterator it = queue_.begin();
while (it != queue_.end()) {
Message *msg = *it;
if (what == msg->what) {
delete msg;
it = queue_.erase(it);
continue;
}
++it;
}
pthread_mutex_unlock(&queue_mutex_);
}
void MessageQueue::Dump() {
std::ostringstream os;
std::list<Message *>::iterator it = queue_.begin();
while (it != queue_.end()) {
Message *msg = *it;
os << msg->what<<"\n";
++it;
}
LOGI("Result=%s", os.str().c_str());
}
}
5.message
message.h
#include "handler.h"
#include <string>
namespace thread {
class Handler;
class Message {
public:
Message();
~Message();
public:
int what;
int arg1;
int arg2;
int arg3;
int arg4;
int arg5;
int arg6;
int arg7;
void *obj1;
void *obj2;
public:
Handler *target;
};
} // namespace thread
message.cc
#include "message.h"
#include "log.h"
namespace thread {
Message::Message()
: what(-1)
, arg1(-1)
, arg2(-1)
, arg3(-1)
, arg4(-1)
, arg5(-1)
, arg6(-1)
, arg7(-1)
, obj1(nullptr)
, obj2(nullptr)
, target(nullptr) {
}
Message::~Message() {
/**
* obj1
* obj2
* target
* 不应该在Message析构函数中销毁, 应该由开发者决定是否销毁
*/
}
}
C++消息队列怎么使用
初始化:
std::string name("AV Message Queue");
thread::HandlerThread *handler_thread = thread::HandlerThread::Create(name);
thread::Handler *handler = new thread::Handler(handler_thread->GetLooper(), this);
同时保证当前的类实现thread::HandlerCallback,实现函数HandleMessage(thread::Message *msg)
不要忘记在析构函数中将handler_thread和handler指针销毁。
发送消息:
thread::Message *msg = new thread::Message();
msg->what = MSG_WHAT;
msg->obj1 = XXXX;
handler->SendMessage(msg);
遗留问题
- 同步等待的消息处理
- 延时消息处理
延时消息处理需要使用链表的结果,目前我们使用的双端队列,不过目前音视频SDK已经够用了,后面如果有变化的时候我会补充的。