Android app加载.c/cpp和.so/.a就必然要谈到jni接口的编写,jni接口注册有俩种方式:动态和静态注册。静态注册的方式固然方便快捷,但是这样的话简单demo可以,为了项目的工程化,还是有必要引入动态注册的,好处会在下面讲(恩,我已经说服了我自己,目前的工作中已经逐步替换为动态注册了)。下面就以一个Kotlin工程为例,逐步梳理下详细过程。
新建工程创建一个Jni接口的class工具类。 这个在工程中external这一行会爆红,鼠标放上去,alt+enter点击creat jni…编译器就会替你生成一个jni接口,这个冗长的函数就是一个静态注册的jni接口函数。同时编译器的error也会消失。 相比于静态注册,动态注册就有以下安全点: 关于动态注册那个冗长的方法,在动态注册时候我们需要把它变短,怎么变短呢?这就牵扯到一个结构体 这个签名有什么用呢?这就牵扯到动态注册中很重要的结构体 说到动态注册就要说道 3.使用 直接贴代码,一切尽在注释中。 大功告成,这是个最简单的动态JNI接口的加载。最后附上代码下载地址 趁着拔智齿在家休息,梳理一遍,准备把JNI基础类型传递,反射,方法调用整理一下,方便自己复制粘贴,加油~NDK笔记(关于Android中Jni的动态注册)
1.静态注册
class JniUtils { external fun stringFromJNI(): String companion object { init { System.loadLibrary("native-lib") } } }
extern "C" JNIEXPORT jstring JNICALL Java_com_heima_jnitest_JniUtils_stringFromJNI(JNIEnv *env, jobject thiz) { // TODO: implement stringFromJNI() }
2.动态注册
我们只有关注2个大点:JNI_OnLoad() 和jniNativeMethod2.1 寻找方法签名
jniNativeMethod
,需要用到类跟方法的签名。我们找到生成的class的文件夹,通过命令javap来找到签名,当然也可以用javah,道理都是一样的。
详细方法入上图所示,找到class→javap→得到签名。得到如下标识:警告: 二进制文件Jniutils包含com.heima.jnitest.JniUtils Compiled from "JniUtils.kt" public final class com.heima.jnitest.JniUtils { public static final com.heima.jnitest.JniUtils$Companion Companion; descriptor: Lcom/heima/jnitest/JniUtils$Companion; public final native java.lang.String stringFromJNI(); descriptor: ()Ljava/lang/String; public com.heima.jnitest.JniUtils(); descriptor: ()V static {}; descriptor: ()V }
JNINativeMethod
其中的signature就是我们通过命令行输出的descriptor,关于网上所说的什么签名对照表什么的,我是记不住,每次敲敲命令行就好,真的没必要查表。jstring stringFromJNI(JNIEnv *env, jobject thiz) {//冗长方法直接删短 // TODO: implement stringFromJNI() } /* 1. typedef struct { const char* name; //函数名字 const char* signature; //函数符号 void* fnPtr; //函数指针 } JNINativeMethod; */ static const JNINativeMethod jniNativeMethod[] = { {"stringFromJNI", "(Ljava/lang/String;)V", (void *) (stringFromJNI)}, };
2.2 JNI_OnLoad
JNI_OnLoad
这个方法,这个方法会在System.loadLibrary("native-lib")
执行的时候就会把方法注册,所以说,提前加载,减少运行时间。我们要做的就是重写他。
jint GetEnv(void** env, jint version)
创建一个JavaVm。第一个参数为创建的指针变量,第二个参数为JNI的NDK版本,非JAVA版本。这个是我们动态注册的关键,同时多线程也会用到它(后续补充)。所以我们升级JavaVm为全局变量通过此方法写入指针,得到指针变量。#define JNI_FALSE 0 #define JNI_TRUE 1 //Jni版本 #define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 //返回值类型 #define JNI_OK (0) /* no error */ #define JNI_ERR (-1) /* generic error */ #define JNI_EDETACHED (-2) /* thread detached from the VM */ #define JNI_EVERSION (-3) /* JNI version error */ #define JNI_ENOMEM (-4) /* Out of memory */ #define JNI_EEXIST (-5) /* VM already created */ #define JNI_EINVAL (-6) /* Invalid argument */ #define JNI_COMMIT 1 /* copy content, do not free buffer */ #define JNI_ABORT 2 /* free buffer w/o copying back */
jclass FindClass(const char* name)
函数通过反射获取到jclass
对象
4. 使用jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)
动态注册jni函数。
JNINativeMethod
集合;JNINativeMethod
的长度,也就是要动态加载的函数的数量/** * 1.设置jvm全局变量,多线程需要用到 * 2.nullptr: C++11后,要取代NULL,作用是可以给初始化的指针赋值 */ JavaVM *jvm = nullptr; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *javaVm, void *pVoid) { jvm = javaVm; // 1.通过JavaVM 创建全新的JNIEnv JNIEnv *jniEnv = nullptr; // 2.判断创建是否成功 jint result = javaVm->GetEnv(reinterpret_cast<void **>(&jniEnv),JNI_VERSION_1_6); // 参数2:是JNI的版本 NDK 1.6 JavaJni 1.8 if (result != JNI_OK) { return -1; // 主动报错 } // 3.找到需要动态动态注册的Jni类 jclass jniClass = jniEnv->FindClass("com/heima/jnitest/JniUtils"); //动态注册(这里就需要用到签名后的方法了) 待注册class 方法集合 方法数量 jniEnv->RegisterNatives(jniClass, jniNativeMethod,sizeof(jniNativeMethod) / sizeof(JNINativeMethod)); return JNI_VERSION_1_6; }
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算