前言
在上一篇使用纯Java代码实现Android Handler消息机制文章中,我们主要研究是在Java代码中如何使用Android Handler,其实Android Handler机制在Android源码中也使用了C++代码实现了一套,也可以方便的在C++代码中使用。
本文提到的源码在此下载:https://gitee.com/cqupt/cpp_android_handler。此项目可以在Android和Linux上直接运行使用。
前景回顾
在Android Handler的Java代码中有几个native方法,我们主要来看一下nativeWake()和nativePollOnce()方法。参考文章:Android framework学习(2)——Handler Native层。
nativeWake()方法
nativeWake用于唤醒功能,在添加消息到消息队列enqueueMessage(), 或者把消息从消息队列中全部移除quit(),再有需要时都会调用 nativeWake方法。包含唤醒过程的添加消息的调用链,如下:
即Java代码通过jni调用了/frameworks/base/core/jni/android_os_MessageQueue.cpp
中的方法。
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
再到/system/core/libutils/Looper.cpp
中的wake()
方法。
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
mWakeEventFd, strerror(errno));
}
}
}
nativePollOnce()方法
nativePollOnce用于提取消息队列中的消息,提取消息的调用链,如下:
同样Java代码通过jni调用了/frameworks/base/core/jni/android_os_MessageQueue.cpp
中的方法。
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
再到/system/core/libutils/Looper.cpp
中的pollOnce()
方法。
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
...
result = pollInner(timeoutMillis);
}
其实相关的代码基本都在/system/core/libutils/Looper.cpp
中,也就是Android的低层库libutils
中(注意NDK中不包含,但可以直接拿源码来用)。
Android libutils源码地址:
https://android.googlesource.com/platform/system/core/+/master/libutils/。
关于libutils
的更多信息可以查看:Android的底层库libutils介绍。接下来我们尝试将源码下载下来编译并使用。
实践一下
Looper
Looper使用到的相关类:
libutils/
├── Looper.cpp
├── RefBase.cpp 引用计数
├── SharedBuffer.cpp 共享内存
├── StrongPointer.cpp 智能指针
├── Threads.cpp 线程
├── Timers.cpp 时间相关
└── VectorImpl.cpp 向量的实现
Native层的消息机制:
Looper.cpp
中主要包含Looper
类和MessageHandler
类,以及Message
结构体。
1、Message
结构体仅有一个属性int what
;
2、Looper.cpp
中没有Handler
类,sendMessage()
等方法直接在Looper
类中实现。Looper.cpp
中sendMessage()
方法与Java的Handler.sendMessage()
类似;
3、Looper.cpp
中也不包含MessageQueue
,而是使用Vector<MessageEnvelope>
一个Vector
来装载消息。
Looper.cpp
的使用和Java端的非常相似,下面我们来看下具体如何使用。
在Android源码中找到一个HandlerThread
类,它继承与Android的Thread
类,关于Thread
的使用,可以参考:Android Framework中的线程Thread及它的threadLoop方法。
HandlerThread
threadLoop()
HandlerThread
的threadLoop()
方法,线程启动后运行,类似Java中run()
方法。
bool HandlerThread::threadLoop()
{
mLock.lock();
mLooper = Looper::prepare(0);
mLooperWait.broadcast();
mLock.unlock();
while (true)
{
do
{
Mutex::Autolock autoLock(mLock);
if (mShouldQuit)
{
return false;
}
} while (false);
mLooper->pollOnce(-1);
}
return false;
}
1、Looper.cpp
中的prepare()
函数:
sp<Looper> Looper::prepare(int opts) {
bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
sp<Looper> looper = Looper::getForThread();
if (looper == NULL) {
looper = new Looper(allowNonCallbacks);
Looper::setForThread(looper);
}
if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
ALOGW("Looper already prepared for this thread with a different value for the "
"LOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
}
return looper;
}
这里的代码跟Java非常相似,在Looper::prepare(0);
时,生成一个looper
对象,并使用pthread_setspecific()
函数,将此对象和该线程绑定。更多介绍,请查看:pthread_setspecific函数与pthread_getspecific函数
2、mLooper->pollOnce(-1);
就是Java侧nativePollOnce()调用的方法,此处作用就是在线程中等待消息。
getLooper()
sp<Looper> HandlerThread::getLooper()
{
Mutex::Autolock autoLock(mLock);
if (mLooper.get() == 0)
{
mLooperWait.wait(mLock);
}
return mLooper;
}
此方法直接返回Looper::prepare(0);
的对象。
示例
一个简单的测试用例,展示如何使用Looper做线程间的通讯:
#include "log.h"
#include "HandlerThread.h"
#include <unistd.h>
#include <utils/Looper.h>
namespace android
{
class MainMessageHandler;
bool mShouldQuit;
sp<Looper> threadLooper;
sp<Looper> mainLooper;
sp<MainMessageHandler> mainMessageHandler;
class ThreadMessageHandler : public MessageHandler
{
public:
virtual void handleMessage(const Message &message)
{
LOGD("ThreadMessageHandler message->what = %d", message.what);
// do something what you want
usleep(10 * 1000);
// HandlerThread程调用mainLooper的sendMessage,发送一个Message()到主线程
mainLooper->sendMessage(mainMessageHandler, Message(message.what));
}
};
class MainMessageHandler : public MessageHandler
{
public:
virtual void handleMessage(const Message &message)
{
LOGD("MainMessageHandler message->what = %d", message.what);
// 结束标志位
if (message.what == 5)
{
mShouldQuit = true;
}
}
};
int test()
{
LOGD("start test");
// 开启`HandlerThread`线程
sp<HandlerThread> mHandlerThread = new HandlerThread();
LOGD("HandlerThread start");
mHandlerThread->start("VMS.NATIVE_LOOP");
// 从线程中获取`threadLooper`对象
threadLooper = mHandlerThread->getLooper();
// new ThreadMessageHandler()里面重载了handleMessage(),会在消息来后回调
sp<ThreadMessageHandler> threadMessageHandler = new ThreadMessageHandler();
// 主线程调用threadLooper的sendMessage,发送一个Message(1)到HandlerThread线程
threadLooper->sendMessage(threadMessageHandler, Message(1));
// 发送一个延时消息
threadLooper->sendMessageDelayed(ms2ns(100), threadMessageHandler, Message(2));
threadLooper->sendMessage(threadMessageHandler, Message(3));
// 主线程构造一个mainLooper
mainLooper = Looper::prepare(0);
// new MainMessageHandler()里面重载了handleMessage(),会在消息来后回调
mainMessageHandler = new MainMessageHandler();
// 主线程调用mainLooper的sendMessage,发送一个Message(4)到自己线程
mainLooper->sendMessage(mainMessageHandler, Message(4));
// 主线程调用mainLooper的sendMessageDelayed,发送一个延时Message(5)到自己线程
mainLooper->sendMessageDelayed(ms2ns(1000), mainMessageHandler, Message(5));
// 主线程开启循环等待消息
while (true)
{
if (mShouldQuit)
{
break;
}
mainLooper->pollOnce(-1);
}
// 结束HandlerThread
mHandlerThread->quit();
LOGD("test end");
return 0;
}
} // namespace android
int main(int argc, char const *argv[])
{
android::test();
return 0;
}
食用方法
下载源代码:https://gitee.com/cqupt/cpp_android_handler
其中的源码来自Android 10的代码,精简了没有使用的CPP文件。在编译Linux平台时,使用EmptyLog.cpp
空实现了Android Log的打印。
根目录下ndk-build.sh
文件是Android NDK编译脚本。根目录下linux-build.sh
文件是在Linux中使用clang++编译的脚本,此项目可以在Linux下使用VS Code打开,直接调试代码。