当执行java的native方法时,虚拟机怎么知道要调用so中那个方法呢?这个就需要注册,通过注册把java的方法和so的方法绑定在一起,这样就可以找到对应的方法了,此篇文章仅作为笔记,以防以后忘记
有俩种注册的方式即 静态注册
和动态注册
我们之前自动生成的项目就是静态注册的,我们看下代码
extern "C" JNIEXPORT jstring JNICALL
Java_com_text_ndk1_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
当第一次调用调用java的native方法时,虚拟机会搜索对应的native函数,如果存在就会建立一个关联,以后再次调用,这部分操作就会由虚拟机完成
Java+包名+类名+方法名
其中用下划线进行分割,包名也用下划线进行分割
这样做会有点缺点:
java层通过System.loadLibrary()
方法可以加载一个动态库,此时虚拟机就会调用jni库中的JNI_OnLoad()
函数
jint JNI_OnLoad(JavaVM* vm, void* reserved);
返回值代表,动态库需要的jni版本,如果虚拟机不能识别这个版本,那么就不可以加载这个动态库
目前的返回值有,JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6。
如果动态库没有提供 JNI_OnLoad()
函数会默认使用JNI_VERSION_1_1
版本,但是这个版本太老,很多新函数没有,最好返回版本
JNI_OnLoad()
函数经常用来做一些初始化操作,动态注册就是在这里进行的
动态注册通过_JNIEnv
的RegisterNatives()
函数来完成
函数原型为
jint RegisterNatives(JNIEnv *env, jclass clazz,
const JNINativeMethod *methods, jint nMethods);
JNINativeMethod
结构体数组,JNINativeMethod
定义了java层函数和native层函数的映射关系返回值 0代表成功,负值代表失败
RegisterNatives
函数最重要的就是JNINativeMethod
结构体,我们看下这个结构体
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
现在我们有一个java类,里面有俩个native方法,下面我们来完成这俩个方法的动态注册
public class TextJni {
static {
System.loadLibrary("textjni_lib");
}
native int text(String message);
static native int static_text(String message);
}
首先手动书写C的对应的方法
jint native_text(JNIEnv *env, jobject jobject1, jstring msg)
jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg)
native_text
对应java层的text
方法,native_staic_text
对应java层的static_text
方法
那么是如何写出来的的呢?
然后需要填充JNINativeMethod
static const JNINativeMethod nativeMethod[] = {
{
"text", "(Ljava/lang/String;)I", (void *) native_text},
{
"static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};
这个上面已经讲过了
然后开始注册
static int registNativeMethod(JNIEnv *env) {
int result = -1;
//找到native方法所在的class
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
这样就动态注册成功
java代码
public class TextJni {
static {
System.loadLibrary("textjni_lib");
}
native int text(String message);
static native int static_text(String message);
}
C代码
#include
#include
#include
jint native_text(JNIEnv *env, jobject jobject1, jstring msg) {
const char *p_msg = env->GetStringUTFChars(msg, JNI_FALSE);
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);
return 0;
}
jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg) {
const char *p_msg = env->GetStringUTFChars(meg, JNI_FALSE);
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);
return 0;
}
static const JNINativeMethod nativeMethod[] = {
{
"text", "(Ljava/lang/String;)I", (void *) native_text},
{
"static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
调用
public class MainActivity extends AppCompatActivity {
// 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);
TextJni.static_text("我是静态方法,哈哈");
new TextJni().text("我是普通方法,哈哈");
}
}
看下打印信息
mmm: method = native_staic_text, msg = 我是静态方法,哈哈
mmm: method = native_text, msg = 我是普通方法,哈哈
https://juejin.im/post/5d1f16f16fb9a07ee46382e1#heading-9
https://blog.csdn.net/afei__/article/details/81031965
https://www.jianshu.com/p/b71aeb4ed13d