JNI源码分析(并实现JNI动态注册)
2017-10-16 13:57
489 查看
本篇来自 微信公众号郭霖 中 李樟清 的投稿,分析了Java和C++语言如何通过so文件交互的,希望对大家有所帮助!
李樟清 的博客地址: http://blog.csdn.net/urrjdg
C/C++的编译和链接
c/c++ ========= 二进制文件
对于C/C++ 一般分为两个阶段
1. 编译
xxx.c ——> windows .obj ; Linux .o –》 语法检查
2. 链接
.o —–> log.so .dll .exe
举例:
a.c a.h b.c b.h
a.c –>b.h(test方法)
在编译阶段只会去找b.h有没有test方法,而在链接的阶段,他会在b.o当中去找这个test方法
如果没有test方法会 报 LinkErro 错误。而这个 LinkErro 错误一般是因为我们在一个文件当中引入了一个.h文件,并且使用了这个文件当中的这个方法,而这个对应的.h文件对应的.o文件(中间文件)里面没有这个方法的实现体。
编译器
将这个C/C++编译链接生成二进制文件的这个过程是谁做的?
是 编译器,编译规则:
Eclipse
GUN编译器 ----> 编译规则 Android.mk (log.so是android自带的)
Android Studio
LLVM编译器 ----> 编译规则 CMakeList.txt
使用Android Studio 创建工程
android studio 会给我们提供一个 exceptiosns support 异常支持
javah 生成头文件
jvm 是虚拟机内存,C/C++ 是 native内存,并且这个 so库 是放在 apk 的 lib 下面的
那这个so库 ,系统是怎么找到的?System.loadLibrary是怎么来找到的?并且系统是如何来区分(JVM是怎么来区分 native
方法(diff)和 javaDiff方法)
native 关键字起到什么作用?loadLibrary
做了什么?
当我们调用 javaDiff 的时候会到 Java虚拟机 的内存当中来处理找这个方法,而加了 native 关键字的时候他就会去到 C++ 的堆栈空间找这个 C++ 的实现。
为什么 native 会这样,起了什么作用?
先在看声明了 native 的方法和没有声明 native 方法之间的区别。
使用 javap -s -p -v FileUtils.class。找到这两个方法,可以看到这两个方法的区别在于 flag ,native 声明的方法 多了个 ACC_NATIVE 的 flag。也就是说 java 在执行这个文件的时候 ,对于有 ACC_NATIVE 的 flag 的方法,他就会去 native
区间去找,如果没有ACC_NATIVE 这个 flag 就在本地的虚拟机空间来找这个方法。
System.loadLibrary找到so库文件分析
native的方法栈为什么能被jvm调用到?从 System.loadLibrary 入手
System.java
Runtime.java
所以可以想到 应该是 ClassLoader的实现类去实现了这个 findLibrary方法。
怎么找是哪个实现类 实现的呢?
从上面可以看出是 PathClassLoader。PathClassLoader .java 这里面没有 findLibrary 继续进到 BaseDexClassLoader
BaseDexClassLoader.java
DexPathList.java
首先我们先来看
DexPathList .java 中的 String fileName = System.mapLibraryName(libraryName);
System.java 看注释可以看出 ,是根据你的平台来找你的 so库
再继续看 for (Element element : nativeLibraryPathElements)
DexPathList .java 可以看到 nativeLibraryPathElements 是在 DexPathList的构造函数里面初始化的
System.loadLibrary加载so库分析
分析下他是怎么加载so库的
现在回到 Runtime.java 的 loadLibrary0 方法找到他的 doLoad 方法
doLoad 方法
nativeLoad 方法 要去 runtime.c(java_lang_Runtime.cc)android-7.1.0_r1.7z\android-7.1.0_r1\libcore\ojluni\src\main\native\runtime.c
以下是 Runtime.c 的源码
下面就是 OpenjdkJvm.cc
Java_vm_ext.h
关键是与JVM的联系:android进程,有且只有一个 JavaVMExt* 指针对象,当我们在 LoadNativeLibrary 的时候,new 了一个 SharedLibrary 的对象指针,而 SharedLibrary 保存了 handle 句柄,然后在找文件方法的时候,都是通过对象里面的 handle
句柄来进行操作的,library 有一个 FindSymbol 来找方法,找到 JNI_OnLoad 方法去做具体的调用,这就是JNI设计的流程
JNI动态注册
根据以上的分析进行实现
静态注册:
每个 class 都需要使用 javah 生成一个头文件,并且生成的名字很长书写不便;初次调用时需要依据名字搜索对应的JNI层函数来建立关联关系,会影响运行效率。用 javah 生成头文件方便简单
javah 生成一个头文件,操作简单
名字很长,书写不方便
初次调用的使用,需要依据名字搜索对应的 FindSymbol(具体看 Runctime.c)
来找到对应的方法,如果方法数较多的时候,效率不高
动态注册:
第一次调用效率高
使用一种数据结构 JNINativeMethod 来记录 java native函数 和 JNI函数 的对应关系
移植方便,便于维护(一个java文件中有多个native方法,只要修改下gMethods 的映射关系)
由于原文过长,本文进行了一些适当的修剪。想要阅读完整文章的朋友,请点击的下方阅读原文,到作者的博客当中查看。
李樟清 的博客地址: http://blog.csdn.net/urrjdg
C/C++的编译和链接
c/c++ ========= 二进制文件
对于C/C++ 一般分为两个阶段
1. 编译
xxx.c ——> windows .obj ; Linux .o –》 语法检查
2. 链接
.o —–> log.so .dll .exe
举例:
a.c a.h b.c b.h
a.c –>b.h(test方法)
在编译阶段只会去找b.h有没有test方法,而在链接的阶段,他会在b.o当中去找这个test方法
如果没有test方法会 报 LinkErro 错误。而这个 LinkErro 错误一般是因为我们在一个文件当中引入了一个.h文件,并且使用了这个文件当中的这个方法,而这个对应的.h文件对应的.o文件(中间文件)里面没有这个方法的实现体。
编译器
将这个C/C++编译链接生成二进制文件的这个过程是谁做的?
是 编译器,编译规则:
Eclipse
GUN编译器 ----> 编译规则 Android.mk (log.so是android自带的)
Android Studio
LLVM编译器 ----> 编译规则 CMakeList.txt
使用Android Studio 创建工程
android studio 会给我们提供一个 exceptiosns support 异常支持
javah 生成头文件
public class FileUtils { public static native void diff(String path,String pattern_Path,int file_num); public static void javaDiff(String path,String pattern_Path,int file_num){} // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } }
jvm 是虚拟机内存,C/C++ 是 native内存,并且这个 so库 是放在 apk 的 lib 下面的
那这个so库 ,系统是怎么找到的?System.loadLibrary是怎么来找到的?并且系统是如何来区分(JVM是怎么来区分 native
方法(diff)和 javaDiff方法)
native 关键字起到什么作用?loadLibrary
做了什么?
当我们调用 javaDiff 的时候会到 Java虚拟机 的内存当中来处理找这个方法,而加了 native 关键字的时候他就会去到 C++ 的堆栈空间找这个 C++ 的实现。
为什么 native 会这样,起了什么作用?
先在看声明了 native 的方法和没有声明 native 方法之间的区别。
使用 javap -s -p -v FileUtils.class。找到这两个方法,可以看到这两个方法的区别在于 flag ,native 声明的方法 多了个 ACC_NATIVE 的 flag。也就是说 java 在执行这个文件的时候 ,对于有 ACC_NATIVE 的 flag 的方法,他就会去 native
区间去找,如果没有ACC_NATIVE 这个 flag 就在本地的虚拟机空间来找这个方法。
C:\Users\Zeking\Desktop\Lsn9\app\src\main\java\com\example\zeking\lsn9>javap -s -p -v FileUtils.class Classfile /C:/Users/Zeking/Desktop/Lsn9/app/src/main/java/com/example/zeking/lsn9/FileUtils.class Last modified 2017-9-2; size 469 bytes MD5 checksum 19201ed5479758e0dfffb63528653a65 Compiled from "FileUtils.java" public class com.example.zeking.lsn9.FileUtils minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #5.#16 // java/lang/Object."<init>":()V #2 = String #17 // native-lib #3 = Methodref #18.#19 // java/lang/System.loadLibrary:(Ljava/lang/String;)V #4 = Class #20 // com/example/zeking/lsn9/FileUtils #5 = Class #21 // java/lang/Object #6 = Utf8 <init> #7 = Utf8 ()V #8 = Utf8 Code #9 = Utf8 LineNumberTable #10 = Utf8 diff #11 = Utf8 (Ljava/lang/String;Ljava/lang/String;I)V #12 = Utf8 javaDiff #13 = Utf8 <clinit> #14 = Utf8 SourceFile #15 = Utf8 FileUtils.java #16 = NameAndType #6:#7 // "<init>":()V #17 = Utf8 native-lib #18 = Class #22 // java/lang/System #19 = NameAndType #23:#24 // loadLibrary:(Ljava/lang/String;)V #20 = Utf8 com/example/zeking/lsn9/FileUtils #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/System #23 = Utf8 loadLibrary #24 = Utf8 (Ljava/lang/String;)V { public com.example.zeking.lsn9.FileUtils(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 public static native void diff(java.lang.String, java.lang.String, int); descriptor: (Ljava/lang/String;Ljava/lang/String;I)V flags: ACC_PUBLIC, ACC_STATIC, ACC_NATIVE // 这边多了个 ACC_NATIVE 代表是native public static void javaDiff(java.lang.String, java.lang.String, int); descriptor: (Ljava/lang/String;Ljava/lang/String;I)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=0, locals=3, args_size=3 0: return LineNumberTable: line 11: 0 static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=0, args_size=0 0: ldc #2 // String native-lib 2: invokestatic #3 // Method java/lang/System.loadLibrary:(Ljava/lang/String;)V 5: return LineNumberTable: line 15: 0 line 16: 5 } SourceFile: "FileUtils.java"
System.loadLibrary找到so库文件分析
native的方法栈为什么能被jvm调用到?从 System.loadLibrary 入手
System.loadLibrary("native-lib");
System.java
public static void loadLibrary(String libname) { Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname); }
Runtime.java
synchronized void loadLibrary0(ClassLoader loader, String libname) { if (libname.indexOf((int)File.separatorChar) != -1) { throw new UnsatisfiedLinkError( "Directory separator should not appear in library name: " + libname); } String libraryName = libname; if (loader != null) { // 点进去发现是return null;找到so库的全路径 String filename = loader.findLibrary(libraryName); if (filename == null) { // It's not necessarily true that the ClassLoader used // System.mapLibraryName, but the default setup does, and it's // misleading to say we didn't find "libMyLibrary.so" when we // actually searched for "liblibMyLibrary.so.so". throw new UnsatisfiedLinkError(loader + " couldn't find \"" + System.mapLibraryName(libraryName) + "\""); } String error = doLoad(filename, loader); if (error != null) { throw new UnsatisfiedLinkError(error); } return; } String filename = System.mapLibraryName(libraryName); List<String> candidates = new ArrayList<String>(); String lastError = null; for (String directory : getLibPaths()) { String candidate = directory + filename; candidates.add(candidate); if (IoUtils.canOpenReadOnly(candidate)) { String error = doLoad(candidate, loader); if (error == null) { return; // We successfully loaded the library. Job done. } lastError = error; } } if (lastError != null) { throw new UnsatisfiedLinkError(lastError); } throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); }
所以可以想到 应该是 ClassLoader的实现类去实现了这个 findLibrary方法。
怎么找是哪个实现类 实现的呢?
Log.i(TAG,this.getClassLoader().toString()); dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.zeking.lsn9-1/base.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_5_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_6_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_7_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_8_apk.apk", zip file "/data/app/com.example.zeking.lsn9-1/split_lib_slice_9_apk.apk"], nativeLibraryDirectories=[/data/app/com.example.zeking.lsn9-1/lib/arm64, /data/app/com.example.zeking.lsn9-1/base.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_dependencies_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_0_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_1_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_2_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_3_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_4_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_5_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_6_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_7_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_8_apk.apk!/lib/arm64-v8a, /data/app/com.example.zeking.lsn9-1/split_lib_slice_9_apk.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]]
从上面可以看出是 PathClassLoader。PathClassLoader .java 这里面没有 findLibrary 继续进到 BaseDexClassLoader
public class PathClassLoader extends BaseDexClassLoader { ...... }
BaseDexClassLoader.java
DexPathList.java
首先我们先来看
DexPathList .java 中的 String fileName = System.mapLibraryName(libraryName);
System.java 看注释可以看出 ,是根据你的平台来找你的 so库
再继续看 for (Element element : nativeLibraryPathElements)
DexPathList .java 可以看到 nativeLibraryPathElements 是在 DexPathList的构造函数里面初始化的
public DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory) { ...... // 找so库是从两个地方来找, // 1.在BaseDexClassLoader初始化的时候传入的目录 这个目录是 librarySearchPath,这个就是应用apk下面的解压的lib目录下 // 2. 在系统的环境变量里面,System.getProperty("java.library.path"):这个目录通过Log.i(TAG,System.getProperty("java.library.path")); // 打印出来是/vendor/lib64:/system/lib64 或者 /vendor/lib:/system/lib // dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.zeking.lsn9-1.apk"], // nativeLibraryDirectories=[/data/app-lib/com.example.zeking.lsn9-1, /system/lib]]] // /data/app-lib/com.example.zeking.lsn9-1, // /system/lib this.nativeLibraryDirectories = splitPaths(librarySearchPath, false); // 这个是系统里面 java.library.path this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true); List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); // 就是在这边进行初始化的 this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories,suppressedExceptions,definingContext); ...... }
System.loadLibrary加载so库分析
分析下他是怎么加载so库的
现在回到 Runtime.java 的 loadLibrary0 方法找到他的 doLoad 方法
synchronized void loadLibrary0(ClassLoader loader, String libname) { if (libname.indexOf((int)File.separatorChar) != -1) { throw new UnsatisfiedLinkError( "Directory separator should not appear in library name: " + libname); } String libraryName = libname; if (loader != null) { String filename = loader.findLibrary(libraryName); // 找到so库的全路径 if (filename == null) { // It's not necessarily true that the ClassLoader used // System.mapLibraryName, but the default setup does, and it's // misleading to say we didn't find "libMyLibrary.so" when we // actually searched for "liblibMyLibrary.so.so". throw new UnsatisfiedLinkError(loader + " couldn't find \"" + System.mapLibraryName(libraryName) + "\""); } String error = doLoad(filename, loader); if (error != null) { throw new UnsatisfiedLinkError(error); } return; } String filename = System.mapLibraryName(libraryName); List<String> candidates = new ArrayList<String>(); String lastError = null; for (String directory : getLibPaths()) { String candidate = directory + filename; candidates.add(candidate); if (IoUtils.canOpenReadOnly(candidate)) { String error = doLoad(candidate, loader); if (error == null) { return; // We successfully loaded the library. Job done. } lastError = error; } } if (lastError != null) { throw new UnsatisfiedLinkError(lastError); } throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); }
doLoad 方法
private String doLoad(String name, ClassLoader loader) { if (loader != null && loader instanceof BaseDexClassLoader) { BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader; librarySearchPath = dexClassLoader.getLdLibraryPath(); } synchronized (this) { // 这一边 return nativeLoad(name, loader, librarySearchPath); } } // 这一边 private static native String nativeLoad(String filename, ClassLoader loader, String librarySearchPath);
nativeLoad 方法 要去 runtime.c(java_lang_Runtime.cc)android-7.1.0_r1.7z\android-7.1.0_r1\libcore\ojluni\src\main\native\runtime.c
以下是 Runtime.c 的源码
#include "jni.h" #include "jni_util.h" #include "jvm.h" #include "JNIHelp.h" #define NATIVE_METHOD(className, functionName, signature) \ { #functionName, signature, (void*)(className ## _ ## functionName) } JNIEXPORT jlong JNICALL Runtime_freeMemory(JNIEnv *env, jobject this) { return JVM_FreeMemory(); } JNIEXPORT jlong JNICALL Runtime_totalMemory(JNIEnv *env, jobject this) { return JVM_TotalMemory(); } JNIEXPORT jlong JNICALL Runtime_maxMemory(JNIEnv *env, jobject this) { return JVM_MaxMemory(); } JNIEXPORT void JNICALL Runtime_gc(JNIEnv *env, jobject this) { JVM_GC(); } JNIEXPORT void JNICALL Runtime_nativeExit(JNIEnv *env, jclass this, jint status) { JVM_Exit(status); } // 这个就是 nativeLoad 方法 的实现 JNIEXPORT jstring JNICALL Runtime_nativeLoad(JNIEnv *env, jclass ignored, jstring javaFilename, jobject javaLoader, jstring javaLibrarySearchPath) { // JVM_NativeLoad 方法 在 OpenjdkJvm.cc 中 return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath); } static JNINativeMethod gMethods[] = { // 使用了一个 NATIVE_METHOD 的 宏替换 ,这个宏替换在这个类的顶部 NATIVE_METHOD(Runtime, freeMemory, "!()J"), NATIVE_METHOD(Runtime, totalMemory, "!()J"), NATIVE_METHOD(Runtime, maxMemory, "!()J"), NATIVE_METHOD(Runtime, gc, "()V"), NATIVE_METHOD(Runtime, nativeExit, "(I)V"), NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)" "Ljava/lang/String;"), }; void register_java_lang_Runtime(JNIEnv *env) { jniRegisterNativeMethods(env, "java/lang/Runtime", gMethods, NELEM(gMethods)); }
下面就是 OpenjdkJvm.cc
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader, jstring javaLibrarySearchPath) { ScopedUtfChars filename(env, javaFilename); if (filename.c_str() == NULL) { return NULL; } std::string error_msg; // 这边 有一个 JavaVMExt , 这个方法的参数有一个 JNIEnv 。 // 那好,JavaVM* 和 JNIEnv 有什么区别呢? // JavaVM* : 一个android应用的进程,有且仅有一个javaVm // JNIEnv :每个java线程都对应一个env的环境变量 // 虚拟机里面jvm 是怎么找到具体的so库的堆栈的? // 他调用了 JavaVM的loadNativeLibrary 方法里面, // 创建了一个结构体(这个结构体,包一个的指针,这个指针放我们真实加载完操作的文件地址) // 在这个结构体里面将我传进来的动态库()filename.c_str())加到结构体里面,然后保存到VM里面, // 那么对于我的android进程其他的地方,我只要拿到这个VM,就能找到这个结构体, // 通过这个结构体,就能找到这个so库里面的方法栈和引用内存 art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM(); // vm->LoadNativeLibrary 方法 在 java_vm_ext.cc bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, javaLibrarySearchPath, &error_msg); if (success) { return nullptr; } }
Java_vm_ext.h
关键是与JVM的联系:android进程,有且只有一个 JavaVMExt* 指针对象,当我们在 LoadNativeLibrary 的时候,new 了一个 SharedLibrary 的对象指针,而 SharedLibrary 保存了 handle 句柄,然后在找文件方法的时候,都是通过对象里面的 handle
句柄来进行操作的,library 有一个 FindSymbol 来找方法,找到 JNI_OnLoad 方法去做具体的调用,这就是JNI设计的流程
JNI动态注册
根据以上的分析进行实现
#include "com_example_zeking_FileUtils.h" #include <android/log.h> #include <assert.h> //int __android_log_print(int prio, const char* tag, const char* fmt, ...) #define TAG "Zeking_JNI" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) /* * Class: com_example_zekign_FileUtils * Method: diff * Signature: (Ljava/lang/String;Ljava/lang/String;I)V */ JNIEXPORT void JNICALL native_diff (JNIEnv *env, jclass clazz, jstring path, jstring pattern_Path, jint file_num){ LOGI("JNI begin 动态注册的方法 "); } static const JNINativeMethod gMethods[] = { { "diff","(Ljava/lang/String;Ljava/lang/String;I)V",(void*)native_diff } }; static int registerNatives(JNIEnv* engv) { LOGI("registerNatives begin"); jclass clazz; clazz = (*engv) -> FindClass(engv, "com/example/zeking/FileUtils"); if (clazz == NULL) { LOGI("clazz is null"); return JNI_FALSE; } if ((*engv) ->RegisterNatives(engv, clazz, gMethods, NELEM(gMethods)) < 0) { LOGI("RegisterNatives error"); return JNI_FALSE; } return JNI_TRUE; } JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){ LOGI("jni_OnLoad begin"); JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGI("ERROR: GetEnv failed\n"); return -1; } assert(env != NULL); registerNatives(env); return JNI_VERSION_1_4; }
静态注册:
每个 class 都需要使用 javah 生成一个头文件,并且生成的名字很长书写不便;初次调用时需要依据名字搜索对应的JNI层函数来建立关联关系,会影响运行效率。用 javah 生成头文件方便简单
javah 生成一个头文件,操作简单
名字很长,书写不方便
初次调用的使用,需要依据名字搜索对应的 FindSymbol(具体看 Runctime.c)
来找到对应的方法,如果方法数较多的时候,效率不高
动态注册:
第一次调用效率高
使用一种数据结构 JNINativeMethod 来记录 java native函数 和 JNI函数 的对应关系
移植方便,便于维护(一个java文件中有多个native方法,只要修改下gMethods 的映射关系)
由于原文过长,本文进行了一些适当的修剪。想要阅读完整文章的朋友,请点击的下方阅读原文,到作者的博客当中查看。
相关文章推荐
- JNI源码分析 (并实现JNI动态注册)
- JNI源码分析 (并实现JNI动态注册)
- JNI源码分析(并实现JNI动态注册)
- android 动态注册JNI函数过程源码分析
- android 动态注册JNI函数过程源码分析
- (七)JNI 源码分析、动态注册
- MTK平台CPU/GPU动态调频的实现之PerfService的源码分析
- Mybatis3源码分析(21)-Mapper实现-动态代理
- JNI实现源码分析【三 间接引用表】
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Android JNI静态和动态注册 、Java Reflect(C或C++层反射和JAVA层反射)、Java 可变参数(JNI实现)
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- Dubbo源码分析(六):Dubbo内核实现之动态编译
- Mybatis3源码分析(21)-Mapper实现-动态代理
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- OpenStack基于Libvirt的虚拟化平台调度实现----Nova虚拟机动态迁移源码分析
- JNI实现源码分析【四 函数调用】
- JNI通过动态注册实现native函数
- 修改MyBatis源码实现扫描注册枚举-分析源码
- OpenStack基于Libvirt的虚拟化平台调度实现----Nova虚拟机动态迁移源码分析