在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})