前言
我们第一篇文章 《[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']
}
}
}
参考文章
- 《Android JNI和NDK学习(03)--动态方式实现JNI》
- 《[NDK开发]Android JNI 开发之第一个 JNI实例》
- 《JNI静态注册和动态注册区别》
- 《你应该了解的JNI知识(一)——静态注册与动态注册》
历史上的今天
暂无评论...
随机推荐
adb push有中文名的apk或者路径会出现名字不全
前言最近在调试时,push的apk目录中带有中问题,就存在push的apk后缀丢失正文偶然apk中带有中文或者存在的路径中带有中,由于疏忽,push后发现apk没有正常启动,最后发现,push的文件存在问题。如下。adb push Desktop\中国125la.apk /sdcard...
Android Studio工程中.idea没有*.iml文件
前言我使用其他同事电脑时,Android Studio(Android Studio Dolphin | 2021.3.1 Patch 1)的版本新建的工程中.idea目录没有对应module和*.iml。百度或谷歌后解决了。记录一下,方便自己查阅。正文解决这个问题很简单,就是Androi...
[摘]Java中String首字母大写方法
摘抄于《Java中String首字母大写方法》,最近自己也写过,使用的很普通的方法。传统思路是:先将String 的首字母单独接取下来转成大写,然后再拼接上剩余字符串。public String upperCase(String str) { return str.substring...
叔本华:比读书更重要的是什么
不管任何藏书丰富的图书馆,假如不加整顿杂乱无章的话,它给予我们的利益还不如那些规模小藏书少,但整理得条理井然、分类清楚的图书馆,同理,不管你学识如何的渊博,如若不能反复思维咀嚼消化的话,它的价值,远逊于那些所知不多但能予以深思熟虑的知识。何以言之?因为我们若要将所学得的知识消化吸收,变为己有,并且能...
我常用的Monkey命令
Monkey的概念“猴子测试”是指没有测试经验的人甚至对计算机根本不了解的人(就像猴子一样)不需要知道程序的任何用户交互方面的知识,如果给他一个程序,他就会针对他看到的界面进行操作,其操作是无目的的、乱点乱按的。这种测试方式在产品周期中的早期阶段会找到很多很好的bug,为用户节省不少的时间。...
[摘]WindowManager.LayoutParams的各种flag含义
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON只要这个window对用户是可见的,则允许在屏幕开启的时候锁定屏幕这个flag可以单独的使用,也可以配合FLAG_KEEP_SCREEN_ON和(或者) FLAG_SHOW_WHEN_LOCKED使用FLAG_DIM_BEHIND...