JNI中调用Java函数
原创
©著作权归作者所有:来自51CTO博客作者Liuqz2009的原创作品,请联系作者获取转载授权,否则将追究法律责任
JNI调用Java函数,主要是在JNI中使用反射调用Java中的函数。
1、Java代码:
package com.my.hawk.jni2;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import static java.lang.String.format;
public class MainActivity extends AppCompatActivity {
TextView tv;
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
nativeInitilize();
Button startBt = findViewById(R.id.button);
startBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nativeThreadStart();
}
});
Button stopBt = findViewById(R.id.button2);
stopBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nativeThreadStop();
}
});
}
public void onNativeCb(int count) {
Log.d("Native", "onNativeCb count=" + count);
// TextView tv = findViewById(R.id.sample_text);
// tv.setText(format("%s%d", stringFromJNI(), count));
tv.post(new Runnable() {
@Override
public void run() {
tv.setText(format("%s%d", stringFromJNI(), count));
}
});
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native void nativeInitilize();
public native void nativeThreadStart();
public native void nativeThreadStop();
}
2、JNI代码
#include <jni.h>
#include <string>
#include <sstream>
#include <android/log.h>
#include <unistd.h>
JavaVM *gJavaVm;
jobject gJaveObj;
static volatile int gIsThreadExit = 0;
#define LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "Native", __VA_ARGS__)
static const char *classPath = "com/my/hawk/jni2/MainActivity";
extern "C" JNIEXPORT jstring JNICALL
Java_com_my_hawk_jni2_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_my_hawk_jni2_MainActivity_nativeInitilize(JNIEnv *env, jobject thiz) {
env->GetJavaVM(&gJavaVm);
gJaveObj = env->NewGlobalRef(thiz);
}
static void* native_thread_exec(void *arg) {
JNIEnv *env;
gJavaVm->AttachCurrentThread(&env, nullptr);
// jclass javaClass = env->FindClass(classPath);
jclass javaClass = env->GetObjectClass(gJaveObj);
if (javaClass == nullptr) {
LOG("Fail to find javaClass");
return nullptr;
}
jmethodID javaCallback = env->GetMethodID(javaClass, "onNativeCb", "(I)V");
if (javaCallback == nullptr) {
LOG("Fail to find method onNativeCb");
return nullptr;
}
LOG("native_thread_exec loop enter");
int count = 0;
while (!gIsThreadExit) {
env->CallVoidMethod(gJaveObj, javaCallback, count++);
sleep(1);
}
gJavaVm->DestroyJavaVM();
LOG("native_thread_exec loop leave");
return nullptr;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_my_hawk_jni2_MainActivity_nativeThreadStart(JNIEnv *env, jobject thiz) {
gIsThreadExit = 0;
pthread_t threadId;
if (pthread_create(&threadId, nullptr, native_thread_exec, nullptr) != 0) {
LOG("native_thread_start pthread_create fail!");
return;
}
LOG("native_thread_start success");
}
extern "C"
JNIEXPORT void JNICALL
Java_com_my_hawk_jni2_MainActivity_nativeThreadStop(JNIEnv *env, jobject thiz) {
gIsThreadExit = 1;
LOG("native_thread_stop success");
}
其中的关键,获取方法,然后通过反射调用native_thread_exec
初始化的时候保存全局JVM和class对象 初始化的时候保存全局
env->GetJavaVM(&gJavaVm);
gJaveObj = env->NewGlobalRef(thiz);
1、Android环境中,每个进程只能诞生一个JavaVM对象,被所有线程共享。在VM加载*.so程序库时,会先调用JNI_OnLoad()函数,在JNI_OnLoad()函数中会将JavaVM指针对象保存到c层JNI的全局变量中。
2、JNIEnv对象和线程是一一对应的关系;
3、Jvm和JNIEnv释放问题?JVM 中 Java Heap 的内存泄漏?JVM 内存中 native memory 的内存泄漏?
4、从操作系统角度看,JVM 在运行时和其它进程没有本质区别。在系统级别上,它们具有同样的调度机制,同样的内存分配方式,同样的内存格局。JVM 进程空间中,Java Heap 以外的内存空间称为 JVM 的 native memory。进程的很多资源都是存储在 JVM 的 native memory 中,例如载入的代码映像,线程的堆栈,线程的管理控制块,JVM 的静态数据、全局数据等等。也包括 JNI 程序中 native code 分配到的资源。
Local Reference 导致的内存泄漏?
参考:Android开发实践:JNI层线程回调Java函数示例 - 指针空间 - 博客园
C++通过JNI层回调java函数 - 百度文库
Android NDK开发(一) - 简书
JNI数据传递
JNIEnv*的常用函数详解
NDK 开发之 Bitmap 的使用-技术圈
JNI层向Java层传递bitmap | 码农家园
JNI String类型 - 知乎
JNI层向Java层传递bitmap | 码农家园
计算Java函数的签名 |
Android开发实践:JNI函数签名生成器 - 行业资讯 - 肥雀云_南京肥雀信息技术有限公司
JNA
jna编程学习 - 走看看
深入浅出JNA