對于入門級Android菜鳥的我來說瘦麸,從配置到開發(fā)JNI是一個煎熬的過程,但還是取得了最終的成功。這里主要是整個過程進(jìn)行了整理达罗,讓其他跟我一樣受煎熬的人盡早跳出來,繼續(xù)向光明邁進(jìn)...
環(huán)境配置
開發(fā)JNI項(xiàng)目前提是需要有NDK(Native Development Kit)的支持静秆。因此粮揉,在開發(fā)前需要先安裝和配置NDK。步驟如下:
點(diǎn)擊菜單"Tools" -> "Android" -> "SDK Manager"打開SDK管理器抚笔。
選中右邊面板的"SDK Tools"頁簽扶认,勾選"NDK"一欄,然后點(diǎn)擊"Apply"來下載并安裝NDK(如下圖)殊橙。
第一個JNI例子
1. 新建Android項(xiàng)目
點(diǎn)擊菜單“File”-“New”-“New Project...”打開新建項(xiàng)目界面, 輸入項(xiàng)目名稱:
選擇支持的平臺及最低支持的系統(tǒng)版本
選擇項(xiàng)目模版Basic Activity
設(shè)置Activity的命名
至此辐宾,創(chuàng)建項(xiàng)目完成。
2. 設(shè)置項(xiàng)目支持JNI
打開gradle.properties文件膨蛮,在該文件下添加:
android.useDeprecatedNdk=true
打開local.properties文件螃概,在該文件下添加:
ndk.dir=NDK的路徑
再打開模塊的build.gradle文件,在android/defaultConfig下面添加ndk節(jié)點(diǎn)鸽疾,如下所示:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "vimfung.cn.jnisample"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
ndk {
moduleName "JNISample"
stl "stlport_static"
ldLibs "log"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
}
其中ndk節(jié)點(diǎn)下面的字段說明如下:
名稱 | 說明 |
---|---|
moduleName | 模塊名稱吊洼,即編譯出來的.so的名字 |
stl | 默認(rèn)情況下JNI中是無法使用STL標(biāo)準(zhǔn)庫的,加入此字段表示使用STL標(biāo)準(zhǔn)庫制肮。 |
ldLibs | "log" 表示加入Android的調(diào)試日志冒窍,只要在導(dǎo)入<pre>#include <android/log.h></pre>就可以使用__android_log_print方法打印日志到logcat中递沪。 |
3. 編寫JNI接口
創(chuàng)建一個叫JNIUtil的Java類,并聲明一個使用native關(guān)鍵字修飾的test方法综液,代碼如下:
package vimfung.cn.jnisample;
/**
* Created by vimfung on 16/9/8.
*/
public class JNIUtil
{
public native String test ();
}
** 注:聲明jni的方法必須帶有native關(guān)鍵字款慨,否則將視為一般的方法。設(shè)置native的方法允許為靜態(tài)/非靜態(tài)方法(即加或不加static關(guān)鍵字)谬莹。**
Command+F9(或菜單“Build”-“Make Project”)進(jìn)行編譯檩奠,成功后點(diǎn)擊界面最下面的Terminal按鈕打開終端面板(終端會自動定位到當(dāng)前項(xiàng)目目錄非常方便o)。使用cd命令跳轉(zhuǎn)到app/build/intermediates/classes/debug/目錄下附帽,輸入腳本如下:
$ cd app/build/intermediates/classes/debug/
使用javah命令生成剛才創(chuàng)建的JNIUtil類的JNI的頭文件(.h文件)埠戳,如:
javah vimfung.cn.jnisample.JNIUtil
** 這里要注意的是javah要根據(jù)包名來對應(yīng)目錄路徑來查找對應(yīng)的.class文件,所以定位的目錄必須要在包目錄結(jié)構(gòu)的上一級(即這里的debug目錄)蕉扮,否則會提示找不到對應(yīng)的類整胃。**
執(zhí)行成功后(執(zhí)行成功是不會輸出任何信息的,錯誤了才會提示-_-#)會在debug目錄下多出一個vimfung_cn_jnisample_JNIUtil.h的頭文件喳钟,如下所示:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class vimfung_cn_jnisample_JNIUtil */
#ifndef _Included_vimfung_cn_jnisample_JNIUtil
#define _Included_vimfung_cn_jnisample_JNIUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: vimfung_cn_jnisample_JNIUtil
* Method: test
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_vimfung_cn_jnisample_JNIUtil_test
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
可以看到剛才定義的test方法在頭文件中被聲明為Java_vimfung_cn_jnisample_JNIUtil_test 方法(其命名規(guī)則我猜應(yīng)該是Java+“”+“包名1”+“” +“包名2”+“”...+“”+“類名”+“_”+“方法名”)屁使。其中有jstring、JNIEnv奔则、jobject這些就是JNI提供給C/C++調(diào)用的類型和接口蛮寂,利用這些東西可以跟Java層進(jìn)行一些交互。
回到Android Studio中易茬,右鍵app目錄共郭,在彈出菜單中選擇“New”-“Folder”-“JNI Folder”建立一個JNI的目錄。如圖:
然后把剛才生成的頭文件拷貝到這個目錄下疾呻,最終效果如圖:
創(chuàng)建一個c++的文件,名字叫vimfung_cn_jnisample_JNIUtil.cpp写半,然后Java_vimfung_cn_jnisample_JNIUtil_test方法進(jìn)行實(shí)現(xiàn)岸蜗,如:
//
// Created by vimfung on 16/9/8.
//
#include "vimfung_cn_jnisample_JNIUtil.h"
JNIEXPORT jstring JNICALL Java_vimfung_cn_jnisample_JNIUtil_test(JNIEnv *env, jobject obj)
{
return env -> NewStringUTF("Hello World!");
}
該方法直接通過env構(gòu)造一個Java的字符串返回值并賦值為“Hello World!”進(jìn)行返回叠蝇。關(guān)于JNI提供的接口功能在后面的章節(jié)會進(jìn)行介紹璃岳,我們這里只要知道是返回字符串就可以了。
現(xiàn)在再打開JNIUtil的Java類悔捶,讓外部一旦使用該類時(shí)即加載JNISample.so這個庫铃慷,修改如下:
package vimfung.cn.jnisample;
/**
* Created by vimfung on 16/9/8.
*/
public class JNIUtil
{
static
{
System.loadLibrary("JNISample");
}
public native String test ();
}
最后,打開MainActivity.java文件蜕该,添加一個JNIUtil的屬性犁柜,并且在onCreate的時(shí)候初始化并調(diào)用test方法。這一步驟主要是驗(yàn)證我們的JNI接口是否正常運(yùn)行堂淡,修改的代碼如下:
public class MainActivity extends AppCompatActivity {
private JNIUtil jniUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
//...此處忽略已有代碼
jniUtil = new JNIUtil();
Log.v("test", jniUtil.test());
}
}
編譯運(yùn)行App馋缅,如果正常編譯運(yùn)行扒腕,可以看到logcat中輸出Hello World!信息萤悴。如圖:
這是Demo的地址:http://git.oschina.net/vimfung/JNISample
JNI類型接口介紹
JNI中定義了一系列的以j開頭的類型瘾腰,下面以表格形式對其進(jìn)行說明:
類型 | 說明 |
---|---|
jboolean | 布爾類型,對應(yīng)一個無符號的char類型 |
jbyte | 字節(jié)類型覆履,對應(yīng)一個有符號的char類型 |
jchar | 字符類型蹋盆,對應(yīng)一個16位無符號整型 |
jshort | 16位整型 |
jint | 32位整型 |
jlong | 64位整型 |
jfloat | 32位浮點(diǎn)型 |
jdouble | 64位浮點(diǎn)型 |
jsize | 等同jint,用于表示大小硝全,長度栖雾。 |
jobject | 表示Java中的Object類型 |
jclass | 表示Java中的Class類型,可以通過env的FindClass方法取得柳沙。 |
jstring | 表示Java中的String類型 |
jarray | 表示Java中的數(shù)組類型 |
jobjectArray | 表示Java中的對象數(shù)組岩灭,如:Object arr[]; |
jbooleanArray | 表示Java中的布爾數(shù)組,如:boolean arr[]; |
jbyteArray | 表示Java中的字節(jié)數(shù)組赂鲤,如:byte arr[]; |
jshortArray | 表示Java中16位整型數(shù)組噪径,如:short arr[]; |
jintArray | 表示Java中32位整型數(shù)組,如:int arr[]; |
jlongArray | 表示Java中64位整型數(shù)組数初,如:long arr[]; |
jfloatArray | 表示Java中32位浮點(diǎn)型數(shù)組找爱,如:float arr[]; |
jdoubleArray | 表示Java中64位浮點(diǎn)型數(shù)組,如:double arr[]; |
jthrowable | 表示Java中的異常對象Exception |
jweak | 表示Java中的弱引用對象 |
jfieldID | 表示Java類中的屬性標(biāo)識符泡孩,可以通過env的GetFieldID或GetStaticFieldID等系列方法取得车摄。 |
jmethodID | 表示Java類中的方法標(biāo)識符,可以通過env的GetMethodID或者GetStaticMethodID方法取得 |
對于每個JNI方法來說通常會有兩個參數(shù)仑鸥,第一個參數(shù)是JNIEnv類型吮播,代表了VM里面的環(huán)境,本地的代碼可以通過該參數(shù)與Java代碼進(jìn)行操作眼俊。第二個參數(shù)是定義JNI方法的類的一個本地引用(this)意狠。
接下來介紹一下JNIEnv會提供一些什么樣的方法(這里只列舉我用過的方法,往后再慢慢補(bǔ)充o):
jclass FindClass(const char* name)
說明:
查找Java的類型疮胖,該方法可以通過傳入一個類名稱來查找對應(yīng)的類型环戈。參數(shù):
name - 類名稱(名稱是以斜桿分隔包名的方式,如:vimfung/cn/jnisample/JNIUtil)返回:
一個類型對象澎灸。使用該對象可以取得其屬性院塞、方法。
jobject NewGlobalRef(jobject obj)
說明:
創(chuàng)建一個全局引用對象性昭。一旦對象在全局引用拦止,則可以在多個方法中使用。因?yàn)镴ava有其自己的回收機(jī)制糜颠,因此在JNI中不可以使用static來維持對象的生命周期创泄,所以艺玲,需要使用該方法來變更對象的生命周期。如果要釋放全局對象請使用DeleteGlobalRef方法鞠抑。參數(shù):
obj - 需要全局引用的對象饭聚。返回:全局的對象引用
void DeleteGlobalRef(jobject globalRef)
說明:
刪除一個全局的對象引用。主要用于刪除NewGlobalRef創(chuàng)建的對象引用搁拙。參數(shù):
globalRef - 全局的對象引用
void DeleteLocalRef(jobject localRef)
說明:
刪除一個本地的對象引用秒梳。一般情況下env創(chuàng)建的對象都屬于本地引用,如果不調(diào)用該方法箕速,在JNI接口執(zhí)行完畢后也會被自動回收掉酪碘。參數(shù):
localRef - 本地對象引用。
jobject NewLocalRef(jobject ref)
說明:
創(chuàng)建一個新的本地對象引用盐茎。參數(shù):
ref - 對象引用返回:本地對象引用
jobject NewObject(jclass clazz, jmethodID methodID, ...)
說明:
創(chuàng)建一個新的Java對象實(shí)例兴垦。參數(shù):
clazz - 需要創(chuàng)建實(shí)例的類型
methodID - 這里需要傳入構(gòu)造方法的標(biāo)志
... - 構(gòu)造方法的一個或者多個參數(shù)返回:
對象的實(shí)例
jclass GetObjectClass(jobject obj)
說明:
獲取Java對象所屬的類型。相當(dāng)于Java中的getClass方法字柠。參數(shù):
obj - 對象返回:
返回對象的類型
jboolean IsInstanceOf(jobject obj, jclass clazz)
說明:
判斷對象是否是指定類型的實(shí)例參數(shù):
obj - 要判斷的對象引用
clazz - 要檢查的類型返回:
1 表示對象為該類型的實(shí)例探越,0 表示對象不是該類型的實(shí)例
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
說明:
由于在調(diào)用Java類某個方法時(shí)需要傳遞方法標(biāo)識,而該方法就是用于獲取指定類的方法的標(biāo)識窑业。參數(shù):
clazz - 類型
name - 方法名稱钦幔,如:value,注:構(gòu)造方法用<init>表示
sig - 方法的簽名常柄,如:(Ljava/lang/Object;)V鲤氢。
對于方法簽名,其組織形式如:(參數(shù)類型1 參數(shù)類型2 ...)返回值類型西潘。其中的類型參考下面章節(jié):JNI的方法/屬性簽名中的類型對照表返回:
方法標(biāo)識(jmethodID)
void CallVoidMethod(jobject obj, jmethodID methodID, ...) void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
說明:
調(diào)用無返回值的方法時(shí)使用該方法卷玉。
其中CallVoidMethodV與CallVoidMethod的區(qū)別是,前者只有三個參數(shù)喷市,其第三個參數(shù)是一個va_list類型相种,用于接收一個參數(shù)列表,該方法常用于被不定項(xiàng)參數(shù)方法嵌套時(shí)使用(即調(diào)用它的方法定義了...參數(shù))东抹。而后者是一個不定項(xiàng)參數(shù)方法。參數(shù):
obj - 要調(diào)用方法的對象實(shí)例
methodID - 方法標(biāo)識沃测,通過GetMethodID獲得缭黔。
... - 方法的參數(shù),可以傳入一個或多個蒂破。args - 參數(shù)列表
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
說明:
獲取類型的屬性標(biāo)識馏谨。在獲取或設(shè)置某個屬性值前,需要先得到屬性的標(biāo)識(jfieldID)附迷,就需要調(diào)用此方法獲取惧互。參數(shù):
clazz - 類型
name - 屬性名稱
sig - 屬性簽名哎媚,其描述方法為:屬性類型。類型請參考下面章節(jié):JNI的方法/屬性簽名中的類型對照表返回:屬性標(biāo)識
jobject GetObjectField(jobject obj, jfieldID fieldID) jboolean GetBooleanField(jobject obj, jfieldID fieldID) jbyte GetByteField(jobject obj, jfieldID fieldID) jchar GetCharField(jobject obj, jfieldID fieldID) jshort GetShortField(jobject obj, jfieldID fieldID) jint GetIntField(jobject obj, jfieldID fieldID) jlong GetLongField(jobject obj, jfieldID fieldID) jfloat GetFloatField(jobject obj, jfieldID fieldID) jdouble GetDoubleField(jobject obj, jfieldID fieldID)
說明:
獲取指定對象的屬性值喊儡。該系列方法的聲明形式遵循“Get+類型+Field”拨与。可以根據(jù)在Java中的不同類型屬性艾猜,選擇不同的方法來獲取屬性值买喧。參數(shù):
obj - 要獲取屬性值的對象實(shí)例
fieldID - 屬性標(biāo)識,可通過GetFieldID獲得匆赃。返回:
屬性值
void SetObjectField(jobject obj, jfieldID fieldID, jobject value) void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value) void SetByteField(jobject obj, jfieldID fieldID, jbyte value) void SetCharField(jobject obj, jfieldID fieldID, jchar value) void SetShortField(jobject obj, jfieldID fieldID, jshort value) void SetIntField(jobject obj, jfieldID fieldID, jint value) void SetLongField(jobject obj, jfieldID fieldID, jlong value) void SetFloatField(jobject obj, jfieldID fieldID, jfloat value) void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
說明:
設(shè)置指定對象的屬性值淤毛。該系列方法的聲明形式遵循“Set+類型+Field”方式,可以根據(jù)在Java中的不同類型算柳,選擇不同的方法進(jìn)行調(diào)用低淡。參數(shù):
obj - 要設(shè)置屬性值的對象實(shí)例
fieldID - 屬性標(biāo)識,可通過GetFieldID獲得瞬项。
value - 屬性值
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
說明:
獲取指定類型的靜態(tài)方法(即帶static關(guān)鍵字描述)的標(biāo)識蔗蹋。參數(shù):
clazz - 要獲取方法標(biāo)識的類型
name - 方法的名稱
sig - 方法的簽名,如:(Ljava/lang/Object;)V滥壕。組織形式跟GetMethodID相同纸颜。返回:
方法標(biāo)識(fmethodID)
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...) void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
說明:
調(diào)用類型的靜態(tài)方法。所調(diào)用的靜態(tài)方法無返回值時(shí)使用绎橘。
其中CallStaticVoidMethodV與CallStaticVoidMethod的區(qū)別是胁孙,前者只有三個參數(shù),其第三個參數(shù)是一個va_list類型称鳞,用于接收一個參數(shù)列表涮较,該方法常用于被不定項(xiàng)參數(shù)方法嵌套時(shí)使用(即調(diào)用它的方法定義了...參數(shù))。而后者是一個不定項(xiàng)參數(shù)方法冈止。參數(shù):
clazz - 調(diào)用方法的類型
methodID - 方法標(biāo)識
... - 方法的參數(shù)狂票,可以是一個或者多個。
args - 參數(shù)列表熙暴。
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
說明:
獲取類型的靜態(tài)屬性(即帶static關(guān)鍵字描述)的標(biāo)識闺属。參數(shù):
clazz - 要獲取屬性標(biāo)識的類型
name - 屬性名稱
sig - 屬性簽名,與GetFieldID相同周霉。
jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) jchar GetStaticCharField(jclass clazz, jfieldID fieldID) jshort GetStaticShortField(jclass clazz, jfieldID fieldID) jint GetStaticIntField(jclass clazz, jfieldID fieldID) jlong GetStaticLongField(jclass clazz, jfieldID fieldID) jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
說明:
獲取指定類型的靜態(tài)屬性值掂器。該系列方法的聲明形式遵循“GetStatic+類型+Field”【阆洌可以根據(jù)在Java中的不同類型屬性国瓮,選擇不同的方法來獲取屬性值。參數(shù):
clazz - 要獲取屬性的類型
fieldID - 屬性標(biāo)識,可通過GetStaticFieldID方法獲得乃摹。返回:
屬性值
void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
說明:
設(shè)置指定類型的屬性值禁漓。該系列方法的聲明形式遵循“SetStatic+類型+Field”方式,可以根據(jù)在Java中的不同類型孵睬,選擇不同的方法進(jìn)行調(diào)用播歼。參數(shù):
clazz - 要設(shè)置屬性的類型
fieldID - 屬性標(biāo)識,可通過GetStaticFieldID方法獲得肪康。
value - 屬性值
jstring NewString(const jchar* unicodeChars, jsize len)
說明:
創(chuàng)建一個Unicode格式的字符串參數(shù):
unicodeChars - 字符串內(nèi)容
len - 字符串長度返回:
字符串對象
jsize GetStringLength(jstring string)
說明:
獲取Unicode格式的字符串長度參數(shù):
string - 字符串返回:
字符串長度
const jchar* GetStringChars(jstring string, jboolean* isCopy)
說明:
將jstring轉(zhuǎn)換成為Unicode格式的char*字符串參數(shù):
string - 要轉(zhuǎn)換的字符串
isCopy - 該參數(shù)用于獲取當(dāng)前的char*字符串是否為字符串對象的一份拷貝荚恶。http://blog.sina.com.cn/s/blog_78eb91cd0102uzv6.html 這篇博文里面對其有描述:當(dāng)從 JNI 函數(shù) GetStringChars 中返回得到字符串B時(shí),如果B是原始字符串java.lang.String 的拷貝磷支,則isCopy被賦值為 JNI_TRUE谒撼。如果B和原始字符串指向的是JVM中的同一份數(shù)據(jù),則 isCopy被賦值為 JNI_FALSE雾狈。當(dāng) isCopy值為JNI_FALSE時(shí)廓潜,本地代碼決不能修改字符串的內(nèi)容,否則JVM中的原始字符串也會被修改善榛,這會打破 JAVA語言中字符串不可變的規(guī)則辩蛋。 通常,因?yàn)槟悴槐仃P(guān)心 JVM 是否會返回原始字符串的拷貝移盆,你只需要為 isCopy傳遞NULL作為參數(shù)悼院。
返回:
Unicode格式的char*字符串
void ReleaseStringChars(jstring string, const jchar* chars)
說明:
釋放指向Unicode格式字符串的char*字符串指針參數(shù):
string - 與char*關(guān)聯(lián)的Unicode格式字符串
chars - 要釋放的char *字符串
jstring NewStringUTF(const char* bytes)
說明:
創(chuàng)建一個UTF-8格式的字符串參數(shù):
bytes - char*字符串返回:
UTF-8格式的字符串對象
jsize GetStringUTFLength(jstring string)
說明:
獲取UTF-8格式字符串的長度參數(shù):
string - UTF-8格式的字符串返回:
字符串長度
const char* GetStringUTFChars(jstring string, jboolean* isCopy)
說明:
將jstring轉(zhuǎn)換成為UTF-8格式的char*字符串參數(shù):
string - 要轉(zhuǎn)換的字符串
isCopy - 該參數(shù)用于獲取當(dāng)前的char*字符串是否為字符串對象的一份拷貝。返回:
UTF-8格式的char*字符串
void ReleaseStringUTFChars(jstring string, const char* utf)
說明:
釋放指向UTF-8格式字符串的char*字符串指針參數(shù):
string - 與char*關(guān)聯(lián)的UTF-8格式字符串
utf - 要釋放的char *字符串
jsize GetArrayLength(jarray array)
說明:
獲取一個數(shù)組所包含的元素?cái)?shù)量參數(shù):
array - 數(shù)組對象返回:
數(shù)組長度
jobjectArray NewObjectArray(jsize length, jclass elementClass, > > > jobject initialElement) jbooleanArray NewBooleanArray(jsize length) jbyteArray NewByteArray(jsize length) jcharArray NewCharArray(jsize length) jshortArray NewShortArray(jsize length) jintArray NewIntArray(jsize length) jlongArray NewLongArray(jsize length) jfloatArray NewFloatArray(jsize length) jdoubleArray NewDoubleArray(jsize length)
說明:
創(chuàng)建一個對象數(shù)組咒循,該系列方法聲明形式遵循“New+類型+Array”据途。可以根據(jù)自己需要保存的類型來調(diào)用不同的方法創(chuàng)建不同類型的數(shù)組叙甸。參數(shù):
length - 數(shù)組長度
elementClass - 元素類型颖医,ObjectArray特有
initialElement - 元素的初始值,ObjectArray特有返回:
數(shù)組對象
jobject GetObjectArrayElement(jobjectArray array, jsize index)
說明:
獲取對象數(shù)組的元素參數(shù):
array - 數(shù)組對象
index - 要獲取元素的下標(biāo)索引返回:
數(shù)組元素對象
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
說明:
設(shè)置對象素組的元素參數(shù):
array - 數(shù)組對象
index - 插入元素的下標(biāo)索引
value - 要設(shè)置的元素
jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy) jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy) jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy) jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy) jint* GetIntArrayElements(jintArray array, jboolean* isCopy) jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy) jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy) jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
說明:
將基礎(chǔ)類型數(shù)組轉(zhuǎn)化為類型指針裆蒸,返回的指針對象可以通過指針偏移來獲取不同下標(biāo)索引的元素(如:(ptr+1)熔萧、(ptr+2)....)。該系列方法聲明遵循“Get+類型+ArrayElements”形式僚祷,可以根據(jù)數(shù)組的類型調(diào)用相對應(yīng)的方法佛致。參數(shù):
array - 基礎(chǔ)類型數(shù)組對象
isCopy - 該參數(shù)用于獲取當(dāng)前的指針是否為數(shù)組的一份拷貝。返回:
數(shù)組指針
void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode) void ReleaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode) void ReleaseCharArrayElements(jcharArray array, jchar* elems, jint mode) void ReleaseShortArrayElements(jshortArray array, jshort* elems, jint mode) void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode) void ReleaseLongArrayElements(jlongArray array, jlong* elems, jint mode) void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode) void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode)
說明:
釋放指向基礎(chǔ)類型數(shù)組的數(shù)組指針辙谜。該系列方法聲明遵循“Release+類型+ArrayElements”形式俺榆,可以根據(jù)數(shù)組的類型調(diào)用相對應(yīng)的方法。參數(shù):
array - 與指針關(guān)聯(lián)的基礎(chǔ)類型數(shù)組
elems - 數(shù)組指針
mode - 釋放模式筷弦,官網(wǎng)對其的說明如下:The last argument to the ReleaseByteArrayElements function above can have the following values:
0: Updates to the array from within the C code are reflected in the Java language copy.
JNI_COMMIT: The Java language copy is updated, but the local jbyteArray is not freed.
JNI_ABORT: Changes are not copied back, but the jbyteArray is freed. The value is used only if the array is obtained with a get mode of JNI_TRUE meaning the array is a copy.
void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf) void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* buf) void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf) void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf) void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf) void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf) void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf)
說明:
獲取指定基礎(chǔ)類型數(shù)組的多個元素肋演。該系列方法聲明遵循“Get+類型+ArrayRegion”形式,可以根據(jù)數(shù)組的類型選擇對應(yīng)的方法進(jìn)行調(diào)用烂琴。取得元素后可以從buf中獲取相應(yīng)的元素爹殊。參數(shù):
array - 基礎(chǔ)類型數(shù)組
start - 要獲取元素的起始下標(biāo)索引
len - 要獲取元素的個數(shù)
buf - 用來存儲元素的指針變量,必須要為指針變量申請內(nèi)存并且內(nèi)存長度要大于獲取內(nèi)容的長度奸绷。
void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean* buf) void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte* buf) void SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar* buf) void SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort* buf) void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf) void SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong* buf) void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat* buf) void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble* buf)
說明:
設(shè)置基礎(chǔ)類型數(shù)組的多個元素值梗夸。該系列方法聲明遵循“Set+類型+ArrayRegion”形式『抛恚可以根據(jù)數(shù)組的類型選擇對應(yīng)的方法進(jìn)行調(diào)用反症。參數(shù):
array - 基礎(chǔ)類型數(shù)組
start - 要替換元素對應(yīng)在array中的起始下標(biāo)索引
len - 替換元素的數(shù)量
buf - 替換元素的指針變量,從該指針中讀取輸入放入數(shù)組中畔派。
jweak NewWeakGlobalRef(jobject obj)
說明:
創(chuàng)建一個全局的弱引用對象參數(shù):
obj - 需要創(chuàng)建弱引用的對象引用返回:
弱引用對象
void DeleteWeakGlobalRef(jweak obj)
說明:
刪除一個全局弱引用參數(shù):
obj - 全局的弱引用對象
JNI的方法/屬性簽名中的類型對照表
標(biāo)記 | 說明 |
---|---|
V | 無類型铅碍,用于表示返回值 |
B | 字節(jié)類型(byte) |
D | 雙精度浮點(diǎn)型(double) |
Z | 布爾類型(boolean) |
I | 整型(int) |
L類型(斜桿分隔包名); | 引用類型,需要以L開頭线椰;結(jié)尾胞谈,如:Ljava/lang/Object; |
[類型(包括基礎(chǔ)類型和引用類型) | 數(shù)組類型,需要以[開頭憨愉,如:[B或者[Ljava/lang/Object; |
給Demo增加一個操作Java對象的接口
一般情況下烦绳,JNI接口都會與Java層進(jìn)行一些交互,那么配紫,就必須要用到env中提供的一些方法實(shí)現(xiàn)這類的需求径密。下面我再添加一個JNI的接口,該接口主要功能是實(shí)現(xiàn)在原生代碼中創(chuàng)建一個HashMap并填充數(shù)據(jù)躺孝,最后返回給Java層享扔。
先打開JNIUtil定義一個新的方法:
public native HashMap createHashMap (String key1, String value1, String key2, String value2);
Command+F9編譯項(xiàng)目,成功后再次使用Terminal對新編譯的JNIUtil生成頭文件(是的括细,每次添加新接口都需要這樣做),然后拷貝到JNI目錄下覆蓋原有文件伪很。生成的頭文件會多出一個新的接口聲明,如下所示:
JNIEXPORT jobject JNICALL Java_vimfung_cn_jnisample_JNIUtil_createHashMap (JNIEnv *, jobject, jstring, jstring, jstring, jstring);
然后再vimfung_cn_jnisample_JNIUtil.cpp中寫入它的方法實(shí)現(xiàn)奋单,代碼如下:
JNIEXPORT jobject JNICALL Java_vimfung_cn_jnisample_JNIUtil_createHashMap
(JNIEnv *env, jobject thiz, jstring key1, jstring value1, jstring key2, jstring value2)
{
//為了方便日后獲取其他地方需要使用HashMap的類型锉试,在這里可以定義為static,然后對jclass創(chuàng)建全局的內(nèi)存引用览濒。
static jclass hashMapClass = NULL;
if (hashMapClass == NULL)
{
jclass hashMapClassLocal = env -> FindClass("java/util/HashMap");
hashMapClass = (jclass)env -> NewGlobalRef(hashMapClassLocal);
env->DeleteLocalRef(hashMapClassLocal);
}
//獲取構(gòu)造方法標(biāo)識和插入數(shù)據(jù)方法標(biāo)識
static jmethodID initMethodId = env -> GetMethodID(hashMapClass, "<init>", "()V");
static jmethodID putMethodId = env -> GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
//創(chuàng)建HashMap對象
jobject hashMapInstance= env -> NewObject(hashMapClass, initMethodId);
//放入對象到HashMap中呆盖,相當(dāng)于調(diào)用HashMap的put方法
env -> CallObjectMethod(hashMapInstance, putMethodId, key1, value1);
env -> CallObjectMethod(hashMapInstance, putMethodId, key2, value2);
return hashMapInstance;
}
上面的代碼完整地演示了如何在JNI中操作一個HashMap,從通過FindClass找到HashMap的類型贷笛,再到GetMethodID獲取HashMap的方法应又,最后通過CallObjectMethod來調(diào)用HashMap的方法來創(chuàng)建對象并放入數(shù)據(jù),這些過程對于操作每個Java類都是必須的乏苦。
這個方法同時(shí)也演示了如何使用NewGlobalRef來創(chuàng)建可跨方法的訪問的對象株扛,其中的hashMapClass正是這樣的處理(只要把hashMapClass放到方法外面定義就可以實(shí)現(xiàn)跨方法訪問了)尤筐。這里要謹(jǐn)記的一點(diǎn)是,只有static描述一個變量是不足以維持jobject的生命周期的洞就,因?yàn)镴VM會管理這些對象的生命周期盆繁,因此,需要使用NewGlobalRef來把一個本地的對象引用提升到全局的對象引用旬蟋,就可以實(shí)現(xiàn)跨方法訪問對象了油昂。
最后再M(fèi)ainActivity的onCreate中調(diào)用createHashMap方法并輸出它的內(nèi)容:
Log.v("create HashMap = %s", jniUtil.createHashMap("Fist Name", "Vim", "Last Name", "Fung").toString());
最后編譯運(yùn)行,如果一切順利的話可以在logcat中看到下面的信息: