JNI 基礎(chǔ) C語言版

靜態(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文件

  1. 在 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);
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者削咆。
  • 序言:七十年代末牍疏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拨齐,更是在濱河造成了極大的恐慌麸澜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奏黑,死亡現(xiàn)場離奇詭異炊邦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)熟史,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門馁害,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹂匹,你說我怎么就攤上這事碘菜。” “怎么了限寞?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵忍啸,是天一觀的道長。 經(jīng)常有香客問我履植,道長计雌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任玫霎,我火速辦了婚禮凿滤,結(jié)果婚禮上妈橄,老公的妹妹穿的比我還像新娘。我一直安慰自己翁脆,他們只是感情好眷蚓,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著反番,像睡著了一般沙热。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罢缸,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天篙贸,我揣著相機(jī)與錄音,去河邊找鬼祖能。 笑死歉秫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的养铸。 我是一名探鬼主播雁芙,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钞螟!你這毒婦竟也來了兔甘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤鳞滨,失蹤者是張志新(化名)和其女友劉穎洞焙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拯啦,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澡匪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了褒链。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唁情。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甫匹,靈堂內(nèi)的尸體忽然破棺而出甸鸟,到底是詐尸還是另有隱情,我是刑警寧澤兵迅,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布抢韭,位于F島的核電站,受9級特大地震影響恍箭,放射性物質(zhì)發(fā)生泄漏刻恭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一季惯、第九天 我趴在偏房一處隱蔽的房頂上張望吠各。 院中可真熱鬧臀突,春花似錦勉抓、人聲如沸贾漏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纵散。三九已至,卻和暖如春隐圾,著一層夾襖步出監(jiān)牢的瞬間伍掀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工暇藏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜜笤,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓盐碱,卻偏偏與公主長得像把兔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瓮顽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345