当前位置: 首页 > article >正文

Android - NDK :JNI实现异步回调

在android代码中,通过JNI调用c层子线程执行耗时任务,在c层子线程中把结果回调到android层,
C语言小白,请批评指正!
使用场景

android层代码:


import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.alibaba.fastjson.JSON;
import com.hisign.callback.MyCallback;
import com.hisign.callback.MyCallbackManager;
import com.hisign.device.databinding.ActivityMainDeviceBinding;

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("broadcast");
    }

    private ActivityMainDeviceBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainDeviceBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnAsync.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MyCallbackManager.getInstance().doAsyncWork(new MyCallback() {
                    @Override
                    public void onStart() {
                        runOnUiThread(() -> Toast.makeText(MainActivity.this, "onStart...", Toast.LENGTH_SHORT).show());
                    }

                    @Override
                    public void onResult(String result) {
                        runOnUiThread(() -> binding.tvDev.setText(result));
                    }

                    @Override
                    public void onEnd() {
                        runOnUiThread(() -> {
                            Toast.makeText(MainActivity.this, "onEnd !!!", Toast.LENGTH_SHORT).show();
                            Log.d("lixm", "run: onEnd...");
                        });
                    }
                });
            }
        });
    }
}

activity_main_device.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btnAsync"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="异步回调"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/tvDev"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:gravity="center_horizontal"
        android:textSize="40sp"
        android:textStyle="bold"/>
</LinearLayout>

MyCallback.java

public interface MyCallback {
    public void onStart();
    public void onResult(String result);
    public void onEnd();
}

MyCallbackManager.java


public class MyCallbackManager {
    private static MyCallbackManager instance;
    private MyCallbackManager(){
    }

    public static MyCallbackManager getInstance(){
        if(instance == null){
            synchronized (MyCallbackManager.class){
                if(instance == null){
                    instance = new MyCallbackManager();
                }
            }
        }
        return instance;
    }

    public native void doAsyncWork(MyCallback callback);
}


jni代码

thread_callback.cpp

#include "callback/ThreadCallback.h"
#include "iostream"
#include <jni.h>
#include "jniLog.h"
using namespace std;

// 在线程里面调用的,都需要声明为全局变量
JavaVM *jvm;
jobject mCallBackObj;

jmethodID resultMid;
jmethodID endMid;
JNIEnv *mEnv;
int mIsDetach = JNI_FALSE;

extern "C"
JNIEXPORT void JNICALL
Java_com_hisign_callback_MyCallbackManager_doAsyncWork(JNIEnv *env, jobject thiz,
        jobject callback) {
    LOGI("native _doAsyncWork...");

    //JavaVM是虚拟机在JNI中的表示,等下再其他线程回调java层需要用到
    env->GetJavaVM(&jvm);
    // 三步曲
    // 1 获取java层的类对象
    mCallBackObj = env->NewGlobalRef(callback);  // 创建一个jni全局引用
    jclass clz = env->GetObjectClass(callback);
    // 2 获取java层的函数
    jmethodID startMid = env->GetMethodID(clz,"onStart", "()V");
    // 这两个方法因为要在子线程中回调,所以声明为全局的
    resultMid = env->GetMethodID(clz,"onResult", "(Ljava/lang/String;)V");
    endMid = env->GetMethodID(clz,"onEnd", "()V");
    // 3 调用函数
    env->CallVoidMethod(mCallBackObj,startMid);

    MyCallback myCallback = [](std::string ret){  // 子线程中计算结果的回调
        //获取当前native线程是否有没有被附加到jvm环境中
        int envStatus = jvm->GetEnv((void **) &mEnv, JNI_VERSION_1_6);
        if(envStatus == JNI_EDETACHED){
            //如果没有, 主动附加到jvm环境中,获取到mEnv
            if (jvm->AttachCurrentThread(&mEnv, NULL) != 0) {
                return;
            }
            mIsDetach = JNI_TRUE;
        }
        // 回调数据到java层
        jstring jstr = mEnv->NewStringUTF(ret.c_str());         // 生成java层的String(jstring)
        mEnv->CallVoidMethod(mCallBackObj, resultMid, jstr);    // 回调到java层
    };
    CallbackEnd cbend = [](){   // 任务完成的回调
        if(mIsDetach){
            mEnv->CallVoidMethod(mCallBackObj,endMid);
            //释放当前线程
            jvm->DetachCurrentThread();
            mEnv = NULL;
        }
    };
    doAsyncWork(myCallback,cbend);
}


ThreadCallback.h

//
// Created by lixm on 2025/1/6.
//
#ifndef BROADCASTNATIVE_THREADCALLBACK_H
#define BROADCASTNATIVE_THREADCALLBACK_H

#include <functional>
typedef std::function<void(std::string)> MyCallback;
typedef std::function<void()> CallbackEnd;
void doAsyncWork(MyCallback MyCallback,CallbackEnd callbackEnd);
#endif //BROADCASTNATIVE_THREADCALLBACK_H

ThreadCallback.cpp

#include <thread>
#include "ThreadCallback.h"
#include "iostream"
#include "../jniLog.h"
#include "unistd.h"
using namespace std;


/**
 * 执行耗时操作
 * @param callback  异步回调
 */
void AsyncWork(MyCallback mycb,CallbackEnd callbackEnd){
    int i = 10;
    while(i -- > 0){
        sleep(1);  // 休眠1秒,模拟耗时操作(unistd.h)
        mycb(to_string(i));
    }
    callbackEnd();
}

void doAsyncWork(MyCallback myCallback,CallbackEnd callbackEnd){
    /**
     * 这里有三个参数
     * 第一个参数是子线程执行的函数
     * 第二个参数是函数的参数(回调计算结果)
     * 第三个参数是函数的参数(回调计算完成)
     */
    thread t1(AsyncWork,myCallback,callbackEnd);
    //t1.join();  // 主线程阻塞,等待线程执行完成
    t1.detach();
}

CmakeLists.txt

cmake_minimum_required(VERSION 3.18.1)

project("broadcast")
add_library( # Sets the name of the library.
        broadcast
        SHARED
        thread_callback.cpp
        callback/ThreadCallback.cpp)

find_library(log-lib log)
target_link_libraries(broadcast ${log-lib})

http://www.kler.cn/a/474232.html

相关文章:

  • 国产游戏崛起,燕云十六移动端1.9上线,ToDesk云电脑先开玩
  • java mail 535 Login Fail. Please enter your authorization code to login
  • (概率论)无偏估计
  • C#异步多线程——ThreadPool线程池
  • Ubuntu上安装Apache Spark
  • C语言冒泡排序教程简介
  • 我用Ai学Android Jetpack Compose之TextField
  • Spring MVC详细介绍
  • 如何查看本地sql server数据库的ip地址
  • oracle创建dblink
  • element(vue2)表格插槽
  • 第十一届蓝桥杯Scratch05月stema选拔赛真题—报数游戏
  • 滑动窗口——串联所有单词的子串
  • Linux好用软件
  • C++ 入门第26天:文件与流操作基础
  • 记录一次MySQL:caching_sha2_password报错
  • Linux中增加swap分区
  • 比QT更高效的一款开源嵌入式图形工具EGT-Ensemble Graphics Toolkit
  • 【gRPC】对称与非对称加解密和单向TLS与双向TLS讲解与go案例
  • vue 点击按钮复制文本功能(同时解决http不安全问题)
  • c# readonly 和 const的区别和使用场景
  • Android配件应用默认启动与USB权限申请区别
  • CODESYS MODBUS TCP通信(禾川Q1 PLC作为MODBUS TCP从站)
  • 【mysql】流程控制
  • 【前端,TypeScript】TypeScript速成(八):Promise
  • 机器学习的组成