# Cpp_Android_Handler **Repository Path**: joonchiu/cpp_android_handler ## Basic Information - **Project Name**: Cpp_Android_Handler - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2024-03-04 - **Last Updated**: 2024-03-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README https://blog.csdn.net/u011057800/article/details/109254866 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201024104425277.png#pic_center) # 前言 在上一篇[使用纯Java代码实现Android Handler消息机制](https://blog.csdn.net/u011057800/article/details/109248788)文章中,我们主要研究是在Java代码中如何使用Android Handler,其实Android Handler机制在Android源码中也使用了C++代码实现了一套,也可以方便的在C++代码中使用。 本文提到的源码在此下载:[https://gitee.com/cqupt/cpp_android_handler](https://gitee.com/cqupt/cpp_android_handler)。此项目可以在Android和Linux上直接运行使用。 # 前景回顾 在Android Handler的Java代码中有几个native方法,我们主要来看一下nativeWake()和nativePollOnce()方法。参考文章:[Android framework学习(2)——Handler Native层](https://blog.csdn.net/u012124438/article/details/107028421)。 ## nativeWake()方法 >nativeWake用于唤醒功能,在添加消息到消息队列enqueueMessage(), 或者把消息从消息队列中全部移除quit(),再有需要时都会调用 nativeWake方法。包含唤醒过程的添加消息的调用链,如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200630162416718.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIxMjQ0Mzg=,size_16,color_FFFFFF,t_70#pic_center) 即Java代码通过jni调用了`/frameworks/base/core/jni/android_os_MessageQueue.cpp`中的方法。 ```cpp static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->wake(); } ``` ```cpp void NativeMessageQueue::wake() { mLooper->wake(); } ``` 再到`/system/core/libutils/Looper.cpp`中的`wake()`方法。 ```cpp 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用于提取消息队列中的消息,提取消息的调用链,如下:![在这里插入图片描述](https://img-blog.csdnimg.cn/20200630141901224.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIxMjQ0Mzg=,size_16,color_FFFFFF,t_70#pic_center) 同样Java代码通过jni调用了`/frameworks/base/core/jni/android_os_MessageQueue.cpp`中的方法。 ```cpp static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); } ``` ```cpp 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()`方法。 ```cpp 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/](https://android.googlesource.com/platform/system/core/+/master/libutils/)。 关于`libutils`的更多信息可以查看:[Android的底层库libutils介绍](https://blog.csdn.net/yu741677868yu/article/details/50589292)。接下来我们尝试将源码下载下来编译并使用。 # 实践一下 ## Looper Looper使用到的相关类: ```bash libutils/ ├── Looper.cpp ├── RefBase.cpp 引用计数 ├── SharedBuffer.cpp 共享内存 ├── StrongPointer.cpp 智能指针 ├── Threads.cpp 线程 ├── Timers.cpp 时间相关 └── VectorImpl.cpp 向量的实现 ``` Native层的消息机制: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200629205706681.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIxMjQ0Mzg=,size_16,color_FFFFFF,t_70#pic_center) `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`一个`Vector`来装载消息。 `Looper.cpp`的使用和Java端的非常相似,下面我们来看下具体如何使用。 在Android源码中找到一个`HandlerThread`类,它继承与Android的`Thread`类,关于`Thread`的使用,可以参考:[Android Framework中的线程Thread及它的threadLoop方法](https://blog.csdn.net/briblue/article/details/51104230)。 ## HandlerThread ### threadLoop() `HandlerThread`的`threadLoop()`方法,线程启动后运行,类似Java中`run()`方法。 ```cpp 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()`函数: ```cpp sp Looper::prepare(int opts) { bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS; sp 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函数](https://blog.csdn.net/mengxingyuanlove/article/details/50820386) 2、`mLooper->pollOnce(-1);` 就是Java侧nativePollOnce()调用的方法,此处作用就是在线程中等待消息。 ### getLooper() ```cpp sp HandlerThread::getLooper() { Mutex::Autolock autoLock(mLock); if (mLooper.get() == 0) { mLooperWait.wait(mLock); } return mLooper; } ``` 此方法直接返回`Looper::prepare(0);`的对象。 ## 示例 一个简单的测试用例,展示如何使用Looper做线程间的通讯: ```cpp #include "log.h" #include "HandlerThread.h" #include #include namespace android { class MainMessageHandler; bool mShouldQuit; sp threadLooper; sp mainLooper; sp 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 mHandlerThread = new HandlerThread(); LOGD("HandlerThread start"); mHandlerThread->start("VMS.NATIVE_LOOP"); // 从线程中获取`threadLooper`对象 threadLooper = mHandlerThread->getLooper(); // new ThreadMessageHandler()里面重载了handleMessage(),会在消息来后回调 sp 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](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打开,直接调试代码。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201024104510117.png?#pic_center)