前言
前面介紹過如何實(shí)現(xiàn)在Android Studio中制作我們自己的so庫(kù),,相信大家看過之后基本清楚如何在Android studio創(chuàng)建JNI函數(shù)并最終編譯成不同cpu架構(gòu)的so庫(kù),但那篇文章介紹注冊(cè)JNI函數(shù)的方法(靜態(tài)方法)存在一些弊端,,本篇將介紹另外一種方法(動(dòng)態(tài)注冊(cè))來克服這些弊端,。
注冊(cè)JNI函數(shù)的兩種方法
靜態(tài)方法
這種方法我們比較常見,但比較麻煩,,大致流程如下: - 先創(chuàng)建Java類,,聲明Native方法,編譯成.class文件,。 - 使用Javah命令生成C/C++的頭文件,,例如:javah -jni com.devilwwj.jnidemo.TestJNI,則會(huì)生成一個(gè)以.h為后綴的文件com_devilwwj_jnidemo_TestJNI.h,。 - 創(chuàng)建.h對(duì)應(yīng)的源文件,,然后實(shí)現(xiàn)對(duì)應(yīng)的native方法,,如下圖所示:
說一下這種方法的弊端:
- 需要編譯所有聲明了native函數(shù)的Java類,每個(gè)所生成的class文件都得用javah命令生成一個(gè)頭文件,。
- javah生成的JNI層函數(shù)名特別長(zhǎng),,書寫起來很不方便
- 初次調(diào)用native函數(shù)時(shí)要根據(jù)函數(shù)名字搜索對(duì)應(yīng)的JNI層函數(shù)來建立關(guān)聯(lián)關(guān)系,這樣會(huì)影響運(yùn)行效率
摘自:深入理解Android卷I
既然有這么多弊端,,我們自然要考慮一下有沒有其他更好的方法下一節(jié)就是我要講的替代方法,,Android用的也是這種方法。
動(dòng)態(tài)注冊(cè)
我們知道Java Native函數(shù)和JNI函數(shù)時(shí)一一對(duì)應(yīng)的,,JNI中就有一個(gè)叫JNINativeMethod的結(jié)構(gòu)體來保存這個(gè)對(duì)應(yīng)關(guān)系,,實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)方就需要用到這個(gè)結(jié)構(gòu)體。舉個(gè)例子,,你就一下子明白了:
聲明native方法還是一樣的:
public class JavaHello {
public static native String hello();
}
創(chuàng)建jni目錄,,然后在該目錄創(chuàng)建hello.c文件,如下:
//
// Created by DevilWwj on 16/8/28.
//
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>
/**
* 定義native方法
*/
JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz)
{
printf("hello in c native code./n");
return (*env)->NewStringUTF(env, "hello world returned.");
}
// 指定要注冊(cè)的類
#define JNIREG_CLASS "com/devilwwj/library/JavaHello"
// 定義一個(gè)JNINativeMethod數(shù)組,,其中的成員就是Java代碼中對(duì)應(yīng)的native方法
static JNINativeMethod gMethods[] = {
{ "hello", "()Ljava/lang/String;", (void*)native_hello},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods) {
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == NULL) {
return JNI_FALSE;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/***
* 注冊(cè)native方法
*/
static int registerNatives(JNIEnv* env) {
if (!registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/**
* 如果要實(shí)現(xiàn)動(dòng)態(tài)注冊(cè),,這個(gè)方法一定要實(shí)現(xiàn)
* 動(dòng)態(tài)注冊(cè)工作在這里進(jìn)行
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if ((*vm)-> GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!registerNatives(env)) { //注冊(cè)
return -1;
}
result = JNI_VERSION_1_4;
return result;
}
先仔細(xì)看一下上面的代碼,看起來好像多了一些代碼,,稍微解釋下,,如果要實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)就必須實(shí)現(xiàn)JNI_OnLoad方法,這個(gè)是JNI的一個(gè)入口函數(shù),,我們?cè)贘ava層通過System.loadLibrary加載完動(dòng)態(tài)庫(kù)后,,緊接著就會(huì)去查找一個(gè)叫JNI_OnLoad的方法。如果有,,就會(huì)調(diào)用它,,而動(dòng)態(tài)注冊(cè)的工作就是在這里完成的。在這里我們會(huì)去拿到JNI中一個(gè)很重要的結(jié)構(gòu)體JNIEnv,,env指向的就是這個(gè)結(jié)構(gòu)體,,通過env指針可以找到指定類名的類,并且調(diào)用JNIEnv的RegisterNatives方法來完成注冊(cè)native方法和JNI函數(shù)的對(duì)應(yīng)關(guān)系,。
我們?cè)谏厦婵吹铰暶髁艘粋€(gè)JNINativeMethod數(shù)組,,這個(gè)數(shù)組就是用來定義我們?cè)贘ava代碼中聲明的native方法,我們可以在jni.h文件中查看這個(gè)結(jié)構(gòu)體的聲明:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
結(jié)構(gòu)體成員變量分別對(duì)應(yīng)的是Java中的native方法的名字,,如本文的hello,;Java函數(shù)的簽名信息、JNI層對(duì)應(yīng)函數(shù)的函數(shù)指針,。
以上就是動(dòng)態(tài)注冊(cè)JNI函數(shù)的方法,,上面只是一個(gè)簡(jiǎn)單的例子,如果你還想再實(shí)現(xiàn)一個(gè)native方法,,只需要在JNINativeMethod數(shù)組中添加一個(gè)元素,,然后實(shí)現(xiàn)對(duì)應(yīng)的JNI層函數(shù)即可,,下次我們加載動(dòng)態(tài)庫(kù)時(shí)就會(huì)動(dòng)態(tài)的將你聲明的方法注冊(cè)到JNI環(huán)境中,而不需要你做其他任何操作,。
總結(jié)
關(guān)于JNI技術(shù),,在Android中使用是非常多的,我們?cè)趯?shí)際開發(fā)中或多或少可能會(huì)使用到第三方或者需要自己開發(fā)相應(yīng)的so庫(kù),,所以學(xué)習(xí)和理解JNI中的一些實(shí)現(xiàn)原理還是很有必要的,,從以前在Eclipse來實(shí)現(xiàn)so庫(kù)開發(fā)到現(xiàn)在可以通過Android Studio來開發(fā)so庫(kù),會(huì)發(fā)現(xiàn)會(huì)方便很多,,這個(gè)也是技術(shù)的發(fā)展帶來的一些便捷,。筆者也計(jì)劃學(xué)習(xí)NDK開發(fā)的相關(guān)技術(shù),后續(xù)也會(huì)將自己學(xué)到的內(nèi)容總結(jié)分享出來,。
|