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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章