靜態(tài)注冊和動態(tài)注冊
看下基本的jni的demo溯饵,我的java代碼
package zcwfeng;
import java.*;
public class Register{
static{
System.load("/Users/zcw/dev/c_workspace/TempC/cmake-build-debug/libfirstlib.dylib");
}
public native String HelloWorld();
public static void main(String[] args){
Register r = new Register();
String re = r.HelloWorld();
System.out.println(re);
}
}
多說兩句儿普,就是可能引入jni.h 的時候不存在需要到j(luò)ava環(huán)境include里面jni頭文件拷貝到項(xiàng)目岛宦,查找一下jni_md.h 也考過來抵皱,搜索一下就可以
我用的clion環(huán)境創(chuàng)建的工程
手動生成頭文件
zcwfeng_Register.h 頭文件余耽,生成的就補(bǔ)貼出來了
zcwfeng_Register.c 實(shí)現(xiàn)缚柏,這個名字可以和頭文件不保持一致,但是這樣比較規(guī)范
#include "zcwfeng_Register.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_zcwfeng_Register_HelloWorld(JNIEnv *env, jobject jobject1){
return (*env)->NewStringUTF(env,"Hello David");
}
靜態(tài)注冊
步驟:
1)編寫java類碟贾,假如是JniTest.java
2)在命令行下輸入 javac JniTest.java 生成JniTest.class文件
- 在 JniTest.class 目錄下 通過 javah xxx.JniTest(全類名)生成 xxx_JniTest.h 頭文件
4)編寫xxx_JniTest.c 源文件船惨,并拷貝xxx_JniTest.h 下的函數(shù),并實(shí)現(xiàn)這些函數(shù)缕陕,且在其中添加jni.h頭文件;
5)編寫 cmakelist.txt 文件粱锐,編譯生成動態(tài)/靜態(tài)鏈接庫
動態(tài)注冊
在Register.java,加入動態(tài)native方法聲明
-> 動態(tài)注冊三個方法聲明
native void dynamicFun1();
native String dynamicFun2();
native int getRandom();
-> main 方法加入調(diào)用
r.dynamicFun1();
System.out.println(r.dynamicFun2());
System.out.println(r.getRandom());
為了方便扛邑,把動態(tài)注冊的代碼提取出來單獨(dú)的c文件實(shí)現(xiàn)怜浅,dyReg.c
#include "jni.h"
void func1(JNIEnv *env, jobject jobject) {
printf("dynamicNative1 動態(tài)注冊");
}
jstring func2(JNIEnv *env, jobject jobject) {
printf("dynamicNative2 動態(tài)注冊");
return (*env)->NewStringUTF(env, "Hello dynamic native");
}
jint func3(JNIEnv *env, jobject jobject) {
return 100;
}
static const char * mClassName = "zcwfeng/Register";
static const JNINativeMethod mMethods[] = {
{"dynamicFun1","()V",(void *)func1},
{"dynamicFun2","()Ljava/lang/String;",(jstring *)func2},
{"getRandom","()I",(jint *)func3}
};
-> 這部分方法參數(shù)和名字不用記,去jni.h 拷貝就行蔬崩,也就是你java調(diào)用load 函數(shù)會自動調(diào)用這里
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
//獲得 JniEnv
int r = (*vm)->GetEnv(vm,(void **) &env, JNI_VERSION_1_4);
if (r != JNI_OK) {
return -1;
}
jclass mainActivityCls = (*env)->FindClass(env,mClassName); // 注冊 如果小于0則注冊失敗
r = (*env)->RegisterNatives(env,mainActivityCls, mMethods, 3);
if (r != JNI_OK) {
return -1;
}
return JNI_VERSION_1_4;
}
完整的java調(diào)用代碼
package zcwfeng;
import java.*;
public class Register{
native void dynamicFun1();
native String dynamicFun2();
native int getRandom();
-> 注意一下這里恶座,windows 的庫是dll,而mac里面是dylib
static{
System.load("/Users/zcw/dev/c_workspace/TempC/cmake-build-debug/libfirstlib.dylib");
}
public native String HelloWorld();
public static void main(String[] args){
Register r = new Register();
String re = r.HelloWorld();
System.out.println(re);
r.dynamicFun1();
System.out.println(r.dynamicFun2());
System.out.println(r.getRandom());
}
}
CMakeList 我的配置參考
cmake_minimum_required(VERSION 3.15.3)
project(TempC)
set(CMAKE_CXX_STANDARD 11)
-> 編譯生成庫沥阳,這里注釋掉
#add_executable(TempC zcwfeng_Register.c zcwfeng_Register.h dyReg.c src/main.cpp)
add_library(firstlib SHARED zcwfeng_Register.h zcwfeng_Register.c dyReg.c)
System.load 和 System.loadLibrary 的區(qū)別
System.load
System.load 參數(shù)必須為庫文件的絕對路徑跨琳,可以是任意路徑,例如: System.load("C:\Documents and Settings\TestJNI.dll"); //Windows
System.load("/usr/lib/TestJNI.so"); //Linux
System.loadLibrary
System.loadLibrary 參數(shù)為庫文件名桐罕,不包含庫文件的擴(kuò)展名脉让。 System.loadLibrary ("TestJNI"); //加載Windows下的TestJNI.dll本地庫 System.loadLibrary ("TestJNI"); //加載Linux下的libTestJNI.so本地庫
注意:TestJNI.dll 或 libTestJNI.so 必須是在JVM屬性java.library.path所指向的路徑中。一般我們老程序員和喜歡是用命令的配置過java環(huán)境沒問題
-------------------------------------憂傷的分割線------------------------
JNIEnv類型和jobject類型的解釋
-> 「這里JNIEXPORT 與 JNICALL 都是JNI的關(guān)鍵字功炮,表示此函數(shù)是要被JNI調(diào)用的溅潜,無需過多解釋」
JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello
(JNIEnv * env, jobject obj) {
printf(hello);
}
JNIEnv* env參數(shù)的解釋
JNIEnv類型 實(shí)際上代表了Java環(huán)境,通過這個JNIEnv* 指針薪伏,就可以對Java端的代碼進(jìn)行操作滚澜。
例如,創(chuàng)建Java類中 的對象嫁怀,調(diào)用Java對象的方法设捐,獲取Java對象中的屬性等等。JNIEnv的指針會被JNI傳入到本地方法的實(shí)現(xiàn)函數(shù)中來對 Java端的代碼進(jìn)行操作塘淑。如下代碼所示
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
......
struct JNIInvokeInterface_;
......
struct JNINativeInterface_ {
void *reserved0;
void *reserved1;
void *reserved2;
void *reserved3;
jint (JNICALL *GetVersion)(JNIEnv *env);
//全是函數(shù)指針
jclass (JNICALL *DefineClass)
(JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
jsize len);
jclass (JNICALL *FindClass)
(JNIEnv *env, const char *name);
jmethodID (JNICALL *FromReflectedMethod)
(JNIEnv *env, jobject method);
jfieldID (JNICALL *FromReflectedField)
(JNIEnv *env, jobject field);
jobject (JNICALL *ToReflectedMethod)
(JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
... }
參數(shù):jobject obj的解釋
如果native方法不是static的話萝招,這個obj就代表這個native方法的類實(shí)例。
如果native方法是static的話朴爬,這個obj就代表這個native方法的類的class對象實(shí)例(static方法不需要類實(shí)例的即寒,所 以就代表這個類的class對象)。
java
public native void test();
public static native void testStatic();
jni代碼.h
->「JNIEnv *, jobject 代表()空參數(shù)」
JNIEXPORT void JNICALL Java_Hello_test
(JNIEnv *, jobject);
JNIEXPORT void JNICALL Java_Hello_testStatic
(JNIEnv *, jclass);
C/C++代碼調(diào)用Java代碼
在JNI中還有 一個非常重要的內(nèi)容,那便是在C/C++本地 代碼中訪問Java端的代碼母赵,一個常見的應(yīng)用就是獲取類的屬性和調(diào)用類的方法逸爵,為了在C/C++中表示屬性和方法,JNI 在jni.h頭文件中定義了jfieldId,jmethodID類型來分別代表Java端的屬性和方法凹嘲。
我們在訪問师倔,或者設(shè)置Java屬性的時候,首先就要先在本地代碼取得代表該Java屬性的jfieldID,然后才能在本地代碼 中進(jìn)行Java屬性操作周蹭,同樣的趋艘,我們需要呼叫Java端的方法時,也是需要取得代表該方法的jmethodID才能進(jìn)行Java 方法調(diào)用凶朗。
使用JNIEnv的:GetFieldID/GetMethodID GetStaticFieldID/GetStaticMethodID 來取得相應(yīng)的jfieldID和jmethodID瓷胧。
下面來具體看一下這幾個方法:
GetFieldID(jclass clazz,const char* name,const char* sign) 方法的參數(shù)說明:
clazz:這個簡單就是這個方法依賴的類對象的class對象 name:這個是這個字段的名稱 sign:這個是這個字段的簽名(我們知道每個變量,每個方法都是有簽名的)
怎么查看類中的字段和方法的簽名
使用javap命令:
javap -s -p JniTes.class
java 字段的簽名
幾個demo里的方法簽名棚愤,也是常用的
int->()I
String->Ljava/lang/String;
void->()V
int [ ] ->[I
float [ ]-> [F
String [ ] -> [Ljava/lang/String;
int [ ][ ] -> [[I
float[ ][ ] -> [[F
Java層方法 JNI函數(shù)簽名
String test ( ) Ljava/lang/String;
int f (int i, Object object) (ILjava/lang/Object;)I
void set (byte[ ] bytes) ([B)V
對應(yīng)于Java端的數(shù)據(jù)類型搓萧,我們也可以看一下下面的表:
Java 類型 類型簽名
boolean Z
byte B
char C
short S
int I
long L
float F
double D
類 L全限定名;,比如String, 其簽名為Ljava/lang/util/String;
數(shù)組 [類型簽名宛畦, 比如 [B
JNI 訪問字符串
- java內(nèi)部使用的是utf-16 16bit 的編碼方式
- jni 里面使用的utf-8 unicode編碼方式 英文是1個字節(jié)瘸洛,中文 3個字節(jié)
- C/C++ 使用 ascii編碼 ,中文的編碼方式 GB2312編碼 中文 2個字節(jié)
public native static String sayHello(String text);
->JNI
JNIEXPORT jstring JNICALL Java_JString_sayHello
(JNIEnv * env, jclass jclaz, jstring jstr) {
const char * c_str = NULL;
char buf[128] = {0};
jboolean iscopy;
c_str = (*env)->GetStringUTFChars(env, jstr, &iscopy);
printf("isCopy:%d\n", iscopy);
if(c_str == NULL) {
return NULL;
}
printf("C_str: %s \n", c_str); sprintf(buf,"Hello, 你好 %s", c_str); printf("C_str: %s \n", buf); (*env)->ReleaseStringUTFChars(env, jstr, c_str); return (*env)->NewStringUTF(env,buf);
}
sayHello函數(shù)接收一個jstring類型的參數(shù)text次和,但jstring類型是指向JVM內(nèi)部的一個字符串反肋,和C風(fēng)格的字符串類型 char*不同,所以在JNI中不能通把jstring當(dāng)作普通C字符串一樣來使用踏施,必須使用合適的JNI函數(shù)來訪問JVM內(nèi)部的字 符串?dāng)?shù)據(jù)結(jié)構(gòu)石蔗。
異常處理
if(c_str == NULL) {
return NULL;
}
以上代碼是很關(guān)鍵的,調(diào)用完GetStringUTFChars之后不要忘記安全檢查读规,因?yàn)镴VM需要為新誕生的字符串分配內(nèi)存空
間抓督,
當(dāng)內(nèi)存空間不夠分配的時候燃少,會導(dǎo)致調(diào)用失敗束亏,失敗后GetStringUTFChars會返回NULL,并拋出一個 OutOfMemoryError異常阵具。
JNI的異常和Java中的異常處理流程是不一樣的碍遍,
Java遇到異常如果沒有捕獲,程序會立即停止 運(yùn)行阳液。
而JNI遇到未決的異常不會改變程序的運(yùn)行流程怕敬,也就是程序會繼續(xù)往下走,這樣后面針對這個字符串的所有操作都是非 常危險的帘皿,
因此东跪,我們需要用return語句跳過后面的代碼,并立即結(jié)束當(dāng)前方法。
釋放字符串
在調(diào)用GetStringUTFChars函數(shù)從JVM內(nèi)部獲取一個字符串之后虽填,JVM內(nèi)部會分配一塊新的內(nèi)存丁恭,用于存儲源字符串 的拷貝,以便本地代碼訪問和修改斋日。即然有內(nèi)存分配牲览,用完之后馬上釋放是一個編程的好習(xí)慣。通過調(diào)用 ReleaseStringUTFChars函數(shù)
通知JVM這塊內(nèi)存已經(jīng)不使用了恶守,你可以清除了第献。注意:這兩個函數(shù)是配對使用的,用 了GetXXX
就必須調(diào)用ReleaseXXX
兔港,而且這兩個函數(shù)的命名也有規(guī)律庸毫,除了前面的Get和Release之外,后面的都一 樣衫樊。
訪問Java 成員變量
非靜態(tài)變量
java代碼
public int property;
Jni代碼
JNIEXPORT void JNICALL Java_Hello_testField(JNIEnv *env, jobject jobj) {
->「首先調(diào)用GetObjectClass函數(shù)獲取ClassField的Class引用」
jclass claz = (*env)->GetObjectClass(env,jobj);
-> 「然后調(diào)用GetFieldID函數(shù)從Class引用中獲取字段的ID(property是字段名岔绸,I是字段的簽名)」
jfieldID jfid = (*env)->GetFieldID(env, claz, "property","I");
-> 「最后調(diào)用GetIntField函數(shù),傳入實(shí)例對象和字段ID橡伞,獲取屬性的值」
jint va = (*env)->GetIntField(env,jobj, jfid);
printf("va: %d", va);
-> 「最后調(diào)用GetIntField函數(shù)盒揉,傳入實(shí)例對象和字段ID,獲取屬性的值」
(*env)->SetIntField(env, jobj, jfid, va + 10086);
}
訪問靜態(tài)變量
訪問靜態(tài)變量和實(shí)例變量不同的是兑徘,獲取字段ID使用GetStaticFieldID刚盈,獲取和修改字段的值使用
Get/SetStaticXXXField系列函數(shù),比如獲取和修改靜態(tài)變量num:
num = (*env)->GetStaticIntField(env,clazz,fid);
// 4.修改靜態(tài)變量num的值
(*env)->SetStaticIntField(env, clazz, fid, 80);
總結(jié)下
1挂脑、由于JNI函數(shù)是直接操作JVM中的數(shù)據(jù)結(jié)構(gòu)藕漱,不受Java訪問修飾符的限制。即崭闲,在本地代碼中可以調(diào)用JNI函數(shù)可以
訪問Java對象中的非public屬性和方法
2肋联、訪問和修改實(shí)例變量操作步聚:
1>、調(diào)用GetObjectClass函數(shù)獲取實(shí)例對象的Class引用
2>刁俭、調(diào)用GetFieldID函數(shù)獲取Class引用中某個實(shí)例變量的ID
3>橄仍、調(diào)用GetXXXField函數(shù)獲取變量的值,需要傳入實(shí)例變量所屬對象和變量ID
4>牍戚、調(diào)用SetXXXField函數(shù)修改變量的值侮繁,需要傳入實(shí)例變量所屬對象、變量ID和變量的值
3如孝、訪問和修改靜態(tài)變量操作步聚:
1>宪哩、調(diào)用FindClass函數(shù)獲取類的Class引用
2>、調(diào)用GetStaticFieldID函數(shù)獲取Class引用中某個靜態(tài)變量ID
3>第晰、調(diào)用GetStaticXXXField函數(shù)獲取靜態(tài)變量的值锁孟,需要傳入變量所屬Class的引用和變量ID
4>彬祖、調(diào)用SetStaticXXXField函數(shù)設(shè)置靜態(tài)變量的值,需要傳入變量所屬Class的引用品抽、變量ID和變量的值
訪問Java中的函數(shù)
Java成員函數(shù)一般有兩類:靜態(tài)和非靜態(tài)涧至。所以在JNI中對這兩種不同的類型就有了兩種不太相同的調(diào)用方法,這兩 種不同類型雖然他們的調(diào)用方式有些許不同桑包,但是南蓬,他們的實(shí)質(zhì)上是一樣的。只是調(diào)用的接口的名字有區(qū)別哑了,而對于 流程是沒有區(qū)別的赘方。
private static void callStaticMethod(String str, int i) {
System.out.format("ClassMethod::callStaticMethod called!-->str=%s,"
+" i=%d\n", str, i);
JNI
JNIEXPORT void JNICALL Java_JniTest_callJavaStaticMethod(JNIEnv *env, jobject jobje){
->「1:從classpath路徑下搜索ClassMethod這個類,并返回該類的Class對象」
jclass clz = (*env)->FindClass(env,"ClassMethod");
if(clz == NULL) {
printf("clz is null");
return;
}
->「2:從clazz類中查找callStaticMethod方法」
jmethodID jmeid = (*env)->GetStaticMethodID(env, clz, "callStaticMethod", "
(Ljava/lang/String;I)V");
if(jmeid == NULL) {
printf("jmeid is null");
return;
}
jstring arg = (*env)->NewStringUTF(env, "我是靜態(tài)類");
->「2:從clazz類中查找callStaticMethod方法」
(*env)->CallStaticVoidMethod(env,clz,jmeid,arg,100);
(*env)->DeleteLocalRef(env,clz);
(*env)->DeleteLocalRef(env,arg);
}
JNI引用
在 JNI 規(guī)范中定義了三種引用:局部引用(Local Reference)弱左、全局引用(Global Reference)窄陡、弱全局引用
(Weak Global Reference)。
局部引用
通過NewLocalRef和各種JNI接口創(chuàng)建(FindClass拆火、NewObject跳夭、GetObjectClass和NewCharArray等)。會阻止GC 回收所引用的對象们镜,不能本地函數(shù)中跨函數(shù)使用币叹,不能跨線程使用。
函數(shù)返回后局部引用所引用的對象會被JVM自動 釋放模狭,或調(diào)用DeleteLocalRef釋放颈抚。 (*env)->DeleteLocalRef(env,local_ref)
jclass cls_string = (*env)->FindClass(env, "java/lang/String");
jcharArray charArr = (*env)->NewCharArray(env, len);
jstring str_obj = (*env)->NewObject(env, cls_string, cid_string, elemArray);
jstring str_obj_local_ref = (*env)->NewLocalRef(env,str_obj); // 通過NewLocalRef函數(shù)創(chuàng)建 ...
釋放一個局部引用有兩種方式:
1、本地方法執(zhí)行完畢后VM自動釋放;
2嚼鹉、通過DeleteLocalRef手動釋放; VM會自動釋放局部引用贩汉,
為什么還需要手動釋放呢?
因?yàn)榫植恳脮柚顾玫膶ο蟊籊C回收。
全局引用
調(diào)用NewGlobalRef基于局部引用創(chuàng)建锚赤,會阻GC回收所引用的對象匹舞。可以跨方法线脚、跨線程使用赐稽。JVM不會自動釋放,
必須調(diào)用DeleteGlobalRef手動釋放 (*env)->DeleteGlobalRef(env,g_cls_string);
tic jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
jclass cls_string = (*env)->FindClass(env, "java/lang/String");
g_cls_string = (*env)->NewGlobalRef(env,cls_string);
}
弱全局引用
調(diào)用NewWeakGlobalRef基于局部引用或全局引用創(chuàng)建酒贬,不會阻止GC回收所引用的對象又憨,可以跨方法、跨線程使 用锭吨。引用不會自動釋放,在JVM認(rèn)為應(yīng)該回收它的時候(比如內(nèi)存緊張的時候)進(jìn)行回收而被釋放寒匙×闳纾或調(diào)用 DeleteWeakGlobalRef手動釋放躏将。(*env)->DeleteWeakGlobalRef(env,g_cls_string)
static jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
jclass cls_string = (*env)->FindClass(env, "java/lang/String");
g_cls_string = (*env)->NewWeakGlobalRef(env,cls_string);
}
拼接字符串例子
->java
public static native String sayHello(String ret);
->jni
JNIEXPORT jstring JNICALL Java_zcwfeng_Register_sayHello(JNIEnv *env,jobject obj,jstring str){
->「將jstring轉(zhuǎn)換成c可以用的char *」
const char * c_str = NULL;
jboolean isCopy;
c_str = (*env)->GetStringUTFChars(env,str,&isCopy);
if(c_str == NULL)
return NULL;
-> 1. malloc 2.初始化 3.釋放----buf[128] = {0} 相當(dāng)于1,2
char buf[128] = {0};
-> c里面的拼接考蕾,std::copy函數(shù)c++
sprintf(buf,"Hello,你好:%s",c_str);
-> 注意這里千萬別忘記
(*env) -> ReleaseStringUTFChars(env,str,c_str);
return (*env)->NewStringUTF(env, buf);
}
獲取Field實(shí)例
package zcwfeng;
public class FieldTest{
static{
System.load("/Users/zcw/dev/c_workspace/TempC/cmake-build-debug/libfirstlib.dylib");
}
public int property = 1;
public static int statProperty = 8;
public native void testField();
public native void testStaticField();
public static void main(String[] args){
FieldTest t = new FieldTest();
t.testField();
t.testStaticField();
System.out.println(t.property);
System.out.println(t.statProperty);
}
}
-> JNI
JNIEXPORT void JNICALL Java_zcwfeng_FieldTest_testField(JNIEnv *env,jobject obj){
// (*env)->FindClass(env,)
jclass clazz = (*env)->GetObjectClass(env,obj);
jfieldID jfid = (*env)->GetFieldID(env,clazz,"property","I");
jint value = (*env)->GetIntField(env,obj,jfid);
printf("java 原始值:%d",value);
(*env)->SetIntField(env,obj,jfid,value + 10000);
}
JNIEXPORT void JNICALL Java_zcwfeng_FieldTest_testStaticField(JNIEnv *env,jobject obj){
// (*env)->FindClass(env,)
jclass clazz = (*env)->GetObjectClass(env,obj);
jfieldID jfid = (*env)->GetStaticFieldID(env,clazz,"statProperty","I");
jint value = (*env)->GetStaticIntField(env,obj,jfid);
printf("java 原始值:%d",value);
(*env)->SetStaticIntField(env,obj,jfid,value + 10000);
}
獲取方法實(shí)例
-> java 靜態(tài)方法和非靜態(tài)方法
package zcwfeng;
public class ClassMethod{
private static void callStaticMethod(String str,int i){
System.out.format("ClassMethod::callStaticMethod called!-str=%s, " + "i=%d\n",str,i);
}
private void callInstanceMethod(String str,int i){
System.out.format("ClassMethod::callInstanceMethod called!-str=%s, " + "i=%d\n",str,i);
}
}
-> JNI
// 調(diào)用java構(gòu)造方法
JNIEXPORT void JNICALL Java_zcwfeng_Register_callJavaStaticMethod(JNIEnv *env,jobject obj){
jclass jclz = (*env) -> FindClass(env,"zcwfeng/ClassMethod");
if(jclz == NULL){
printf("jclz is null");
return;
}
jmethodID jconstructorId = (*env) -> GetMethodID(env,jclz,"<init>","()V");
if(jconstructorId == NULL){
printf("jconstructorId is null");
return;
}
jobject jobj = (*env)->NewObject(env,jclz,jconstructorId);
jmethodID jmid = (*env) -> GetStaticMethodID(env,jclz,"callStaticMethod","(Ljava/lang/String;I)V");
jstring jstr = (*env)->NewStringUTF(env,"I am from JNI zcwfeng");
-> 「注意一下這里CallStaticVoidMethod祸憋,參數(shù)是class而不是jobject」
(*env)->CallStaticVoidMethod(env,jclz,jmid,jstr,1008600);
(*env)->DeleteLocalRef(env,jclz);
(*env)->DeleteLocalRef(env,jobj);
(*env)->DeleteLocalRef(env,jstr);
}
// 調(diào)用java構(gòu)造方法
JNIEXPORT void JNICALL Java_zcwfeng_Register_callJavaInstanceMethod(JNIEnv *env,jobject obj){
jclass jclz = (*env) -> FindClass(env,"zcwfeng/ClassMethod");
if(jclz == NULL){
printf("jclz is null");
return;
}
jmethodID jconstructorId = (*env) -> GetMethodID(env,jclz,"<init>","()V");
if(jconstructorId == NULL){
printf("jconstructorId is null");
return;
}
jobject jobj = (*env)->NewObject(env,jclz,jconstructorId);
jmethodID jmid = (*env) -> GetMethodID(env,jclz,"callInstanceMethod","(Ljava/lang/String;I)V");
jstring jstr = (*env)->NewStringUTF(env,"I am from JNI David\n");
(*env)->CallVoidMethod(env,jobj,jmid,jstr,10086);
(*env)->DeleteLocalRef(env,jclz);
(*env)->DeleteLocalRef(env,jobj);
(*env)->DeleteLocalRef(env,jstr);
}
野指針實(shí)例
特別注意局部引用static的情況釋放
DeleteLocalRef
并將對應(yīng)的 引用智控
區(qū)別什么是引用,GetMethodID方法不是
-> java 本地方法
public native String JCallC();
public native String newString(int len);
肖卧。蚯窥。。
Register r = new Register();
r.JCallC();
r.JCallC();
-> 邏輯是 JCallC()調(diào)用一個jni方法塞帐,jni內(nèi)部調(diào)用newString方法
-> JNI
JNIEXPORT jstring JNICALL Java_zcwfeng_Register_newString(JNIEnv *env, jobject obj, jint len) {
jcharArray elementArray;
jstring j_str = NULL;
static jclass cls_string = NULL;
static jmethodID jmetid = NULL;
if (cls_string == NULL) {
printf("cls_string is null \n");
cls_string = (*env)->FindClass(env, "java/lang/String");
if (cls_string == NULL) {
return NULL;
}
}
if (jmetid == NULL) {
printf("jmetid is null \n");
jmetid = (*env)->GetMethodID(env, cls_string, "<init>", "([C)V");
if (jmetid == NULL) {
return NULL;
}
}
printf("this is a line ----------------------- \n");
elementArray = (*env)->NewCharArray(env, len);
printf("this is a line2 ----------------------- \n");
printf("this is a %d,%d,%d\n", cls_string, jmetid, elementArray);
j_str = (*env)->NewObject(env, cls_string, jmetid, elementArray);
printf("this is a line3 ----------------------- \n");
(*env)->DeleteLocalRef(env, elementArray);
elementArray = NULL;
(*env)->DeleteLocalRef(env, cls_string);
-> 「static靜態(tài)引用拦赠,注釋掉這句,下次在調(diào)用JCallC葵姥,就會導(dǎo)致野指針問題荷鼠,因?yàn)殚_始判斷為NULL,創(chuàng)建榔幸,現(xiàn)在沒有=NULL允乐,就會導(dǎo)致:指針指向的內(nèi)存塊內(nèi)容為空」
cls_string = NULL;
-> 「這句是錯的因?yàn)閖metid---GetMethodID 不是LocakRef 他不是引用類型」
//////// (*env)->DeleteLocalRef(env,jmetid);
jmetid = NULL;
printf("end of function\n");
return j_str;
}
JNIEXPORT jstring JNICALL Java_zcwfeng_Register_JCallC(JNIEnv *env, jobject obj) {
return Java_zcwfeng_Register_newString(env, obj, 1024);
}