前言
我们第一篇文章 《[NDK开发]Android JNI 开发之第一个 JNI实例》就是静态注册的。
除了有静态注册,还有动态注册。
静态注册
静态注册:先由Java得到本地方法的声明,然后再通过JNI实现该声明方法。
- 优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
- 缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高
动态注册
动态注册:动态注册JAVA的Native方法,使得c/c++里面方法名 可以和 java 的Native方法名可以不同, 动态注册是将将二者方法名关联起来,以后在修改Native方法名时,只需修改动态注册关联的方法名称即可
- 优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
- 缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败
正文
下面简单记录一下动态注册的使用。
系统初始化JNI在加载时,会调用JNI_OnLoad(),而卸载时会调用JNI_UnLoad();所以,我们可以通过重写JNI_OnLoad(),在JNI_OnLoad()中将函数注册到Android中,以便能通过Java访问。
第一步
MyDynamic.java代码
package com.biumall.mydynamic2;
public class MyDynamic {
static {
System.loadLibrary("MyDynamic");
}
/***
* 1. javac MyDynamic2.java
* 2. javap -s MyDynamic2.class
*
* public static native java.lang.String StringFormJNI();
* Signature: ()Ljava/lang/String;
*
* public static native void StringToJNI(java.lang.String);
* Signature: (Ljava/lang/String;)V
*
*/
public static native String StringFormJNI();
public static native void StringToJNI(String message);
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
String TAG= "Dynamic_Android_Log";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate : "+ MyDynamic.StringFormJNI());
MyDynamic.StringToJNI("Hello from Android.");
}
}
第二步
MyDynamic.c
#include<string.h>
#include<jni.h>
#include<stdio.h>
#include <android/log.h>
#define LOG_TAG "Dynamic_JNI_Log"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
//获取数组大小
#define ARRAY_LENGTH(x) ((int)(sizeof(x) / sizeof((x)[0])))
//定义MyDynamic.java类目录(就是包名+类名,只不过.换成了/)
#define DYNAMIC_CLASS "com/biumall/mydynamic2/MyDynamic"
// 定义native 实现的StringFormJNI
JNIEXPORT jstring JNICALL native_StringFormJNI(JNIEnv *env , jclass clasz) {
LOGD("--------native_StringFormJNI----------");
return (*env)->NewStringUTF(env, "Hello form JNI.");
}
// 定义native 实现的StringToJNI
JNIEXPORT void JNICALL native_StringToJNI(JNIEnv *env, jclass clasz, jstring message){
LOGD("native_StringToJNI===%s",(*env)->GetStringUTFChars(env, message, 0));
}
//定义Java和JNI函数的绑定表
// 方法数组,分别为:
// 方法名 | 方法签名 | 函数指针
// 可以通过javac和javap 获取 方法签名
JNINativeMethod method_table[]= {
{"StringFormJNI", "()Ljava/lang/String;", (void*) native_StringFormJNI},
{"StringToJNI", "(Ljava/lang/String;)V", (void *) native_StringToJNI},
};
int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod * methods, int methods_size){
jclass clazz = NULL;
//反射Java类
clazz = (*env)->FindClass(env, className);
if(NULL == clazz){
return JNI_ERR;
}
int result = (*env)->RegisterNatives(env, clazz, methods, methods_size);
LOGD("--------registerNativeMethods---------- result : %d", result);
if(result < 0){
return JNI_ERR;
}
return JNI_OK;
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){
JNIEnv * env = NULL;
int result = (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6);
LOGD("--------JNI_OnLoad----------1 result : %d", result);
if(result != JNI_OK){
return JNI_ERR;
}
result = registerNativeMethods(env , DYNAMIC_CLASS, method_table, ARRAY_LENGTH(method_table));
LOGD("--------JNI_OnLoad----------2 result : %d", result);
if(result != JNI_OK){
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
第四步
Android.mk和Application.mk,这个代码跟之前一样的。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE := MyDynamic
LOCAL_SRC_FILES := MyDynamic.c
include $(BUILD_SHARED_LIBRARY)
APP_ABI := all
第五步
在工程中的build.gradle,新增如下内容:
android {
compileSdkVersion 31
// 略
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
参考文章
© 版权声明