1、静态注册与动态注册介绍
先说静态注册,此方式为传统做法,JNI函数注册比较方便。在运行期调用JNI函数的时候注册。如下:
/**
* 静态注册
*/
native void staticRegist();
/**
* 此种方式为静态注册
*/
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_jni_103_MainActivity_staticRegist(JNIEnv *env, jobject thiz) {
LOGD("native 静态注册")
}
因为静态注册为调用JNI函数的时候才注册,因此性能会稍微比动态注册低,但是对于日常开发影响不大。
下面说动态注册,在安卓系统源码中,JNI源码基本都是使用动态注册,因为对性能要求比较高,例如:android_os_Parcel.cpp的源码。
2、动态注册实现
动态注册实现的思路是在JNI初始化的函数JNI_OnLoad中进行注册。
// 日志输出
#include
#define TAG "Main_Native"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
#include
#include
#include // 在AS上 pthread不需要额外配置,默认就有
//以下三个函数为动态注册的JNI函数
void dynamicNativeMethod01() {
LOGD("dynamicNativeMethod01")
}
void dynamicNativeMethod02(JNIEnv *jniEnv, jobject thiz, jint number, jstring str) {
const char *content = jniEnv->GetStringUTFChars(str, nullptr);
LOGD("dynamicNativeMethod02 number:%d,str:%s", number, content);
}
int dynamicNativeMethod03(JNIEnv *jniEnv, jobject thiz, jint number, jstring str) {
const char *content = jniEnv->GetStringUTFChars(str, nullptr);
LOGD("dynamicNativeMethod03 number:%d,str:%s", number, content);
return 900;
}
JavaVM *javaVm = nullptr;
const char *className = "com/hvm/vender/jni_03/MainActivity";
//JNINativeMethod数组,注册多个函数
const JNINativeMethod jniNativeMethods[] = {
{"dynamicRegist1", "()V", (void *) (dynamicNativeMethod01)},
{"dynamicRegist2", "(ILjava/lang/String;)V", (void *) (dynamicNativeMethod02)},
{"dynamicRegist3", "(ILjava/lang/String;)I", (int *) (dynamicNativeMethod03)}
};
/**
* JNI_OnLoad 函数,类似JAVA中的构造函数
* System.loadLibrary的时候会调用该函数,可以进行做初始化操作
*/
extern "C"
JNIEXPORT int JNICALL
JNI_OnLoad(JavaVM *javaVm, void *) {
::javaVm = javaVm;
JNIEnv *jniEnv = nullptr;
//获取jniEnv
int result = javaVm->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6);
if (result != JNI_OK) {
LOGD("jniEnv获取失败")
return -1;
}
//获取jclass
jclass j_class = jniEnv->FindClass(className);
//动态注册JNI函数,参数:jclass、函数指针数组(多个函数)、函数数量
jniEnv->RegisterNatives(j_class, jniNativeMethods,
(sizeof(jniNativeMethods)) / (sizeof(JNINativeMethod)));
LOGE("动态 注册成功");
return JNI_VERSION_1_6;
}
/**
* 此种方式为静态注册
*/
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_jni_103_MainActivity_staticRegist(JNIEnv *env, jobject thiz) {
LOGD("native 静态注册")
}
package com.hvm.vender.jni_03;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Parcel;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.hvm.vender.jni_03.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
// Used to load the 'jni_03' library on application startup.
static {
System.loadLibrary("jni_03");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
initListener();
}
private void initListener() {
binding.btnStaticRegist.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//静态注册
staticRegist();
}
});
binding.btnRegist.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//动态注册三个JNI函数
dynamicRegist1();
dynamicRegist2(200, "daxiaa2");
int result = dynamicRegist3(300, "daxiaa3");
Log.d(TAG, "dynamicRegist3: "+result);
}
});
}
/**
* 静态注册
*/
native void staticRegist();
native void dynamicRegist1();
native void dynamicRegist2(int number, String str);
native int dynamicRegist3(int number, String str);
}
3、JNI线程
Java代码
binding.btnThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
threadTest();
}
});
Native代码
JavaVM *javaVm = nullptr;
const char *className = "com/hvm/vender/jni_03/MainActivity";
/**
* JNI_OnLoad 函数,类似JAVA中的构造函数
* System.loadLibrary的时候会调用该函数,可以进行做初始化操作
*/
extern "C"
JNIEXPORT int JNICALL
JNI_OnLoad(JavaVM *javaVm, void *) {
::javaVm = javaVm;
return JNI_VERSION_1_6;
}
/**
* 接收jobject
* @param jobject
* @return
*/
void *run(void *jobject) {
JNIEnv *jniEnv = nullptr;
//给当前线程设置JNIEnv
int result = javaVm->AttachCurrentThread(&jniEnv, nullptr);
if (result != JNI_OK) {
LOGD("给当前线程设置JNIEnv失败")
}
jclass j_class = jniEnv->GetObjectClass(static_cast<_jobject *>(jobject));
jmethodID j_methodId = jniEnv->GetMethodID(j_class, "threadCallBack", "()V");
jniEnv->CallVoidMethod(static_cast<_jobject *>(jobject), j_methodId);
//当前线程移除JNIEnv
javaVm->DetachCurrentThread();
//线程执行函数,记得写返回值,否则崩溃
return nullptr;
};
extern "C"
JNIEXPORT void JNICALL
Java_com_hvm_vender_jni_103_MainActivity_threadTest(JNIEnv *env, jobject thiz) {
//线程ID
pthread_t pid;
//全局引用
jobject jobjectGlobal = env->NewGlobalRef(thiz);
//将jobject提示为全局引用,传递到其他线程
pthread_create(&pid, nullptr, run, jobjectGlobal);
//注意:
//1、jobject为局部引用不能跨线程使用、不能跨函数使用,需要提示为全局引用>NewGlobalRef(thiz);
//2、JavaVM是全局的,和进程绑定。
//4、JNIEnv 不能跨线程,可以跨函数使用。当需要在其他线程使用的时候,
//可以拿到全局的 JavaVM,调用javaVm->AttachCurrentThread,
//使用完之后,移除 javaVm->DetachCurrentThread();
//4、JNIEnv和线程绑定
}
注意
1、jobject为局部引用不能跨线程使用、不能跨函数使用,需要转换为全局引用>NewGlobalRef(thiz);
2、JavaVM是全局的,和进程绑定。
3、JNIEnv 不能跨线程,可以跨函数使用。当需要在其他线程使用的时候,可以拿到全局的 JavaVM,调用
javaVm->AttachCurrentThread(&jniEnv, nullptr);为当前线程绑定JNIEnv
使用完之后,移除JNIEnv ,调用 javaVm->DetachCurrentThread();
4、JNIEnv和线程绑定,JavaVM和进程绑定
5、线程执行函数,记得写返回值,否则崩溃
源码
https://gitee.com/daxiaa/jni-ndk.git