在之前一篇中简单的介绍了一下setDataSource的流程,其中遇到了一个新的消息机制AHandler,其实这东西本来不需要介绍,因为消息机制原本就是一个很成熟和常见的技术技巧,这玩意里面包含了计算机哲学和计算机玄学的双学位问题,听起来牛逼轰轰,其实也就那回事了。为了这次文档的完整性,再一个后面可能要整理到公司的文档库中,所以在此介绍一下,熟悉的同学直接飘过了。
先说说为什么要用消息机制,在我们的软件开发中经常会遇到多线程的问题,由此便带来了线程同步的问题,比如各种锁,最常见的就是Mutex,而线程同步通常是有代价的,这个代价一方面来自于锁本身,而另一方面来自于程序设计,在一个有众多线程的进程中使用同步锁常常造成各种死锁问题,当然你可以说你水平高超没有这个问题,而通常解决锁之间的竞争往往在效率上也要付出相应的代价,当然了你也可以说你连这也能解决,那我也只能膜拜了。然而通常还有另一个问题,便是线程间的执行顺序问题,如果使用锁和条件变量来控制,在线程众多的情况下,可控性只会越来越差,另外通常来讲CPU的利用率也不高。那么消息机制是如何来解决这些问题的呢?
第一个问题是什么是消息机制,这里先介绍一个同步/异步的的概念,举例来说明,
比如说肚子饿了去饭店吃饭,点好菜后你坐那老老实实等着服务员上菜,什么时候菜做了好你才能结束等待,那这就是同步。
而异步就不用干等了,掏出手机,打开外卖软件,下单,OK,这时你就可以去干别的事情了,应该没有人死盯着手机干等吧,到时候饭店做好饭,小哥自然送上门。
听起来异步比同步是先进很多了,但在现实中事情往往没有想象的那么容易,首先你用外卖软件,如果我是老板,餐厅已经爆满了,顾客在饭店里已经骂娘了,哪还顾得上你,第二你点完得等送餐吧,又多了一段路程,而同步也有自己的缺点,坐在餐馆等饭的时间对于你而言算是浪费了,比如你还有其他事情没做此时也只能乖乖干坐着了。
好吧,扯太远了,回到消息机制上来。
当我们想要我们的程序执行一些事情的时候要么同步,要么异步,而消息机制便是一种异步的方式,首先创建一个消息队列,这个消息队列在一个单独的线程中轮询,一旦有人发送消息给它,它就会将消息取出执行,执行后又回到等待状态直到有新的消息到来。
如下图:
消息被投递到一个消息队列中去,而Looper会持续不断的从Message Queue中拿出消息,再投递给合适的Handler去处理。
好了,开始对Android的这个消息队列进行分析吧,依旧从使用上入手,通常在使用的开始会创建一个ALooper对象,然后会实现一个AHandler的子类,最后创建消息进行投递进入Message Queue。
首先我们先看一下AHandler的定义。代码不多直接贴上来。
namespace android {
struct AMessage;
struct AHandler : public RefBase {
AHandler()
: mID(0),
mVerboseStats(false),
mMessageCounter(0) {
}
ALooper::handler_id id() const {
return mID;
}
sp<ALooper> looper() const {
return mLooper.promote();
}
wp<ALooper> getLooper() const {
return mLooper;
}
wp<AHandler> getHandler() const {
// allow getting a weak reference to a const handler
return const_cast<AHandler *>(this);
}
protected:
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
private:
friend struct AMessage; // deliverMessage()
friend struct ALooperRoster; // setID()
ALooper::handler_id mID;
wp<ALooper> mLooper;
inline void setID(ALooper::handler_id id, wp<ALooper> looper) {
mID = id;
mLooper = looper;
}
bool mVerboseStats;
uint32_t mMessageCounter;
KeyedVector<uint32_t, uint32_t> mMessages;
void deliverMessage(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(AHandler);
};
} // namespace android
cpp文件:
namespace android {
void AHandler::deliverMessage(const sp<AMessage> &msg) {
onMessageReceived(msg);
mMessageCounter++;
if (mVerboseStats) {
uint32_t what = msg->what();
ssize_t idx = mMessages.indexOfKey(what);
if (idx < 0) {
mMessages.add(what, 1);
} else {
mMessages.editValueAt(idx)++;
}
}
}
} // namespace android
由于该类为基类,在使用时需要自己定义子类去继承重新类中的方法,所先以该类的Cpp文件相对比较简单,只定义了一个deliverMessage方法,
先看看.h文件中该类的构成,其中有一个容易混淆的成员mMessages被定义为KeyedVector类型,该并不是真正的Message Queue,相反该对象并没有什么太大的实际作用,只有在verbose模式下才存储一个消息被传递的次数,对我们分析没什么帮助。剩下的就是一个id值,和一个ALooper的弱引用。刚刚我们已经说过了,Looper负责将消息源源不断的发送给Handler来处理,所以两者是必然有关联的。另外还有一个mVerboseStats的bool值,这玩意实际上没啥用,先不看了。之后看看一个重要的纯虚函数onMessageReceived,我们定义的子类最重要的就是实现这个方法。之后会通过调用把Message发给这个函数,而这个函数会根据这个Message中的what值来判断需要进行什么操作,习惯用法是通过一个switch来处理。
namespace android {
struct AHandler;
struct AMessage;
struct AReplyToken;
struct ALooper : public RefBase {
typedef int32_t event_id;
typedef int32_t handler_id;
ALooper();
// Takes effect in a subsequent call to start().
void setName(const char *name);
handler_id registerHandler(const