在C++代码中使用Android Handler消息机制(Android or Linux)

在这里插入图片描述

前言

在上一篇使用纯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.cppsendMessage()方法与Java的Handler.sendMessage()类似;

3、Looper.cpp中也不包含MessageQueue,而是使用Vector<MessageEnvelope>一个Vector来装载消息。

Looper.cpp的使用和Java端的非常相似,下面我们来看下具体如何使用。

在Android源码中找到一个HandlerThread类,它继承与Android的Thread类,关于Thread的使用,可以参考:Android Framework中的线程Thread及它的threadLoop方法

HandlerThread

threadLoop()

HandlerThreadthreadLoop()方法,线程启动后运行,类似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打开,直接调试代码。

在这里插入图片描述

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值