Android 如何在jni层使用Looper

Posted Alex_MaHao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 如何在jni层使用Looper相关的知识,希望对你有一定的参考价值。

概述

假设现在有这样一个需求:在c++层进行定时任务,然后任务回调到主线程运行。对于在java层通过handler.postDelay()就可以实现。而在c层呢?

两种思路:

  • 通过jni反调java层的handler方法做处理。
  • 在jni层获取到对应c++主线程的looper,然后进行处理

第一种方式实现上很简单。第二种方式google提供了jni层对应的库,地址https://developer.android.com/ndk/reference/group/looper#group___looper_1ga2668285bfadcf21ef4d371568a30be33

使用

假设想定时检测gps的有无,有如下文件

CheckGpsTask.h


#ifndef C_LOOP_DEMO_CHECKGPSTASK_H
#define C_LOOP_DEMO_CHECKGPSTASK_H


class CheckGpsTask 

public:
    void checkGps();
    virtual void start();
;

#endif //C_LOOP_DEMO_CHECKGPSTASK_H

start()方法用于开启定时任务,checkGps用于在主线程中运行。

定义一个子类androidCheckAdapter,以便后期适配ios

AndroidCheckAdapter.h

#ifndef C_LOOP_DEMO_ANDROIDCHECKADAPTER_H
#define C_LOOP_DEMO_ANDROIDCHECKADAPTER_H

#include <android/looper.h>
#include "CheckGpsTask.h"

class AndroidCheckAdapter : public CheckGpsTask 

public:
    ALooper *mainThreadLooper;
    
    // 用以监听事件
    int readPipe = -1;
    int writePipe = -1;

    AndroidCheckAdapter();

    ~AndroidCheckAdapter();

    void start();
;

#endif //C_LOOP_DEMO_ANDROIDCHECKADAPTER_H

mainThreadLooper是调用jni的默认线程,通过一定方式获取。

pipe是管道相关,用于监听读写事件。

具体实现

AndroidCheckGpsAdapter.cpp

#include "AndroidCheckAdapter.h"

#include <android/looper.h>
#include <unistd.h>
#include <thread>
#include <android/log.h>

#define LOGI(...) __android_log_print(ANDROID_LOG_DEBUG, "loop", __VA_ARGS__)


// 当管道有写入时的回调
int callback(int fd, int events, void *data) 
    char msg;
    read(fd, &msg, 1);
    ((AndroidCheckAdapter *) data)->checkGps();
//    AndroidCheckAdapter::getInstance()->checkGps();
    // 返回1表示持续接收事件
    return 1;


AndroidCheckAdapter::AndroidCheckAdapter() 
    // 初始化操作
    mainThreadLooper = ALooper_forThread();
    if (mainThreadLooper != NULL) 
        ALooper_acquire(mainThreadLooper);
        
        int messagePipe[2];
        int result = pipe(messagePipe);

        if (result == -1) 
            return;
        

        readPipe = messagePipe[0];
        writePipe = messagePipe[1];
        LOGI("readPipe %d - writePipe %d", readPipe, writePipe);
        // 添加监听
        ALooper_addFd(mainThreadLooper, readPipe,
                      0, ALOOPER_EVENT_INPUT, callback, this);
    


AndroidCheckAdapter::~AndroidCheckAdapter() 
    // 释放资源
    if (mainThreadLooper != NULL && readPipe != -1) 
        ALooper_removeFd(mainThreadLooper, readPipe);
        ALooper_release(mainThreadLooper);
    

    if (readPipe != -1) 
        close(readPipe);
    

    if (writePipe != -1) 
        close(writePipe);
    



void AndroidCheckAdapter::start() 
    // 开启子线程
    std::thread worker([this]() 
        for (char msg = 0; msg < 110; msg++) 
            LOGI("send : %s",
                 std::string("tid:").append(std::to_string(gettid())).c_str());
            // 写入消息,更新管道
            write(writePipe, &msg, 1);
            sleep(1);
        
    );
    worker.detach();

按照流程如下:

获取主线程的Looper对象,并获取引用,防止被销毁。

mainThreadLooper = ALooper_forThread();
ALooper_acquire(mainThreadLooper);

创建管道,并监听回调

 int messagePipe[2];
        int result = pipe(messagePipe);

        if (result == -1) 
            return;
        

        readPipe = messagePipe[0];
        writePipe = messagePipe[1];
        LOGI("readPipe %d - writePipe %d", readPipe, writePipe);

        /**
         * ALooper*:mainThreadLooper:loop对象
         * fd:readPipe:监听读入管道
         * ident:主要用于poll的方式监听,如果后面的callback不为null,则该字段被忽视
         * events:ALOOPER_EVENT_INPUT 监听的事件类型
         * ALooper_callbackFunc:callback,当有输入事件时的回调
         * data:数据指针
         */
        ALooper_addFd(mainThreadLooper, readPipe,
                      0, ALOOPER_EVENT_INPUT, callback, this);

开启线程,往管道中写入数据


void AndroidCheckAdapter::start() 
    // 开启子线程
    std::thread worker([this]() 
        for (char msg = 100; msg < 110; msg++) 
            LOGI("send : %s",
                 std::string("tid:").append(std::to_string(gettid())).c_str());
            // 写入数据     
            write(writePipe, &msg, 1);
            sleep(1);
        
    );
    worker.detach();

回调被执行


int callback(int fd, int events, void *data) 
    char msg;
    read(fd, &msg, 1);
    ((AndroidCheckAdapter *) data)->checkGps();
    // 返回1表示持续接收事件
    return 1;

以上是关于Android 如何在jni层使用Looper的主要内容,如果未能解决你的问题,请参考以下文章

Android JNI开发

Android OpenCV实现人脸检测JNI层添加打印时间

Android jni 线程同步

Android jni 线程同步

Android开发实践:JNI层线程回调Java函数示例

Android NDK JNI开发(工具:CMake)