FFmpeg4Android:jni中c/c++調(diào)用java

7 FFmpeg4Android:jni中c/c++調(diào)用java

7.1 c/c++訪問java屬性

先來看一個函數(shù)定義:

JNIEXPORT jstring JNICALL Java_com_test_jni_TestNative_stringFromJNI
  (JNIEnv * env, jobject jobj) {
    return (*env)->NewStringUTF(env, "jni development.");
}

參數(shù)說明:
1)env:是一個結(jié)構體指針的指針,主要用來在C/C++中使用虛擬機的功能,比如說:訪問Java方法、屬性、創(chuàng)建Java對象、處理字符串等等。
2)jobj:是代表對象或類的結(jié)構體
如果native方法不是靜態(tài)方法,jobj代表該方法所屬的java對象
如果native方法是靜態(tài)方法,jobj代表該方法所屬Java類的class對象 //TestNative.class

c與java數(shù)據(jù)類型對應關系
Java基本數(shù)據(jù)類型與JNI數(shù)據(jù)類型的映射關系(在C/C++中用特定的類型來表示Java的數(shù)據(jù)類型),在反射訪問java中屬性時需要指定屬性的簽名,三者的對應關系如下:

java類型 c/c++類型 簽名
boolean jboolean Z
byte jbyte B
char jchar C
short jshort S
int jint I
long jlong L
float jfloat F
double jdouble D
void void V

引用類型(對象)

java類型 c/c++類型 簽名
String jstring Ljava/lang/String;
Object jobject Ljava/lang/Object;
byte[] jByteArray [B
Class jclass

對象類型以L開頭,然后/分隔包的完整類型,后面再加";"。
數(shù)組類型以[開頭,在加上數(shù)據(jù)元素類型的簽名,如int[],簽名就是[I;再比如int[][],簽名就是[[I,object數(shù)組簽名就是[Ljva/lang/Object;。

7.1.1 訪問一般屬性

如有java類JniTest:

package com.lzp.jni;

import java.util.Random;
import java.util.UUID;

public class JniTest {

    public String key = "walker";
    
    public static int count = 9;
    
    public native static String getStringFromC();
    
    public native String getString2FromC(int i);
    // 訪問屬性,返回修改之后的屬性內(nèi)容
    public native String accessField();
    
    public native void accessStaticField();
    
    public native void accessMethod();
    
    public native void accessStaticMethod();
    
    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);
        JniTest t = new JniTest();
        text = t.getString2FromC(6);
        System.out.println(text);
        
        System.out.println("key修改前:"+t.key);
        t.accessField();
        System.out.println("key修改后:"+t.key);
        
        System.out.println("count修改前:"+count);
        t.accessStaticField();
        System.out.println("count修改后:"+count);
        
        t.accessMethod();
        t.accessStaticMethod();
    }
    
    // 產(chǎn)生指定范圍的隨機數(shù)
    public int genRandomInt(int max){
        System.out.println("genRandomInt 執(zhí)行了...");
        return new Random().nextInt(max); 
    }
    
    // 產(chǎn)生UUID字符串
    public static String getUUID(){
        return UUID.randomUUID().toString();
    }
    
    // 加載動態(tài)庫
    static{ 
        System.loadLibrary("jni_study");
    }
}

我們想在jni中訪問該類中的屬性key,并修改。

// 修改屬性key的字符串
JNIEXPORT jstring JNICALL Java_com_tz_jni_TestNative_accessField(JNIEnv * env, jobject obj){
    //得到class
    jclass cls = (*env)->GetObjectClass(env, obj);
    //jfieldID
    //簽名:類型的簡稱
    //屬性,方法
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");

    //獲取key屬性的值
    //注意:key為基本數(shù)據(jù)類型,規(guī)則如下
    //(*env)->GetIntField(); (*env)->Get<Type>Field();
    jstring jstr = (*env)->GetObjectField(env, obj, fid);

    //jstring轉(zhuǎn)為 C/C++字符串
    char *str = (*env)->GetStringUTFChars(env, jstr, NULL);
    //拼接字符串
    char text[50] = "super ";
    strcat(text,str);

    //拼接完成之后,從C字符串轉(zhuǎn)為jstring
    jstr = (*env)->NewStringUTF(env, text);

    //修改key的屬性
    //注意規(guī)則:Set<Type>Field
    (*env)->SetObjectField(env, obj, fid, jstr);

    return jstr;
}

7.1.2訪問靜態(tài)屬性

//count屬性+10
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_accessStaticField(JNIEnv * env, jobject obj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, obj);
    //jfieldID
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
    //獲取靜態(tài)屬性的值
    //規(guī)則:GetStatic<Type>Field
    jint count = (*env)->GetStaticIntField(env, cls, fid);
    count += 10;
    //修改屬性的值
    //規(guī)則:SetStatic<Type>Field
    (*env)->SetStaticIntField(env, cls, fid, count);
}

7.2 c/c++訪問java方法

回顧一下java反射,一般分為3個步驟:
1)加載calss(字節(jié)碼),獲取class的對象;
2)獲取對應的方法或?qū)傩裕?br> 3)修改屬性,或執(zhí)行方法。
例如:
有java類Reflect:

public class Reflect {
    public void print(String s) {
        System. out.println(s);
    }
}

另一個Test類來反射此類,執(zhí)行print(String)方法:

public class Test {
    public static void main(String[] args) {
        try {
            Class clazz = Test.class.getClassLoader().loadClass("Reflect");
            Method method = clazz.getDeclaredMethod("print", new Class[] {String.class});
            method.invoke(clazz.newInstance(), new String[] {"java反射"});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制臺輸出:java反射

接下來看c/c++中的反射。
而對一個方法,其簽名就是其參數(shù)類型簽名和返回值類型簽名的字符串,其形式如下:
**(類型簽名1類型簽名2...)返回值類型簽名 **

下面看看兩個例子:
方法 1):public string addTail(String tail, int index)
方法簽名為:(Ljava/util/String;I)Ljava/util/String;
方法 2):public int addValue(int index, String value,int[] arr)
方法 簽名為:(ILjava/util/String;[I)I

也可以通過命令,獲取屬性與方法簽名。在class文件夾中,輸入:javap -s -p 類名
1)訪問java方法

// 借用java的api產(chǎn)生指定大小的隨機數(shù)
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_accessMethod(JNIEnv * env, jobject obj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, obj);
    //jmethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    //調(diào)用方法,產(chǎn)生了一個隨機數(shù)
    //規(guī)則:Call<Type>Method 返回值類型
    jint random = (*env)->CallIntMethod(env, obj, mid, 200);

    //打印出來看看
    //FILE *fp = fopen("D://log.txt", "w");
    //int轉(zhuǎn)為字符串
    char str[50];
    sprintf(str, "%d", random);
    fputs(str, fp);
    fclose(fp);
}

2)訪問靜態(tài)方法

//借用java api 產(chǎn)生一個UUID字符串,作為文件的名稱
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_accessStaticMethod(JNIEnv * env, jobject cls){    
    //如果native方法為static,jobject為子類jclass的實例,也就是native方法所屬的類的Class實例
    //jclass
    //jclass cls = (*env)->GetObjectClass(env, obj);

    //jmethodID
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;"); 

    //調(diào)用
    //規(guī)則:CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);
    //jstring轉(zhuǎn)為C字符串
    char *uuid_str = (*env)->GetStringUTFChars(env, uuid, NULL);
    char filename[100] = {0};
    sprintf(filename, "D://%s.txt", uuid_str);
    FILE *fp = fopen(filename, "w");
    fputs(uuid_str, fp);
    fclose(fp);
}

3)訪問構造方法

//使用java.util.Date產(chǎn)生一個當前時間時間戳
JNIEXPORT jobject JNICALL Java_com_tz_jni_TestNative_accessConstructor(JNIEnv * env, jobject obj){
    //Date jclass
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //構造方法jmethodID
    jmethodID contructor_mid = (*env)->GetMethodID(env, cls, "<init>","()V");
    //實例化一個Date對象
    jobject date_obj = (*env)->NewObject(env, cls, contructor_mid);

    //調(diào)用getTime方法
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, mid);
    //jlong -> 字符串
    FILE *fp = fopen("D://log.txt", "w");
    char str[50];
    sprintf(str, "%lld", time);
    fputs(str, fp);
    fclose(fp);

    return date_obj;
}

4)調(diào)用父類的方法

JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_callNonvirtualMethod(JNIEnv * env, jobject obj) {
    //獲取一個Man對象
    jclass cls = (*env)->GetObjectClass(env, obj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/tz/jni/Human;");
    jobject human_obj = (*env)->GetObjectField(env, obj, fid);

    //執(zhí)行sayHi方法
    jclass human_cls = (*env)->FindClass(env, "com/tz/jni/Human");//注意:傳父類的類名
    jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()Ljava/lang/String;");
    
    //執(zhí)行子類覆蓋的方法
    //jstring jstr = (*env)->CallObjectMethod(env, human_obj, mid);
    //執(zhí)行父類的方法
    jstring jstr = (*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);

    //jstring->char*
    char * str = (*env)->GetStringUTFChars(env, jstr, NULL);
    FILE *fp = fopen("D://log.txt", "w");
    fputs(str, fp);
    fclose(fp);
}

7.3 實戰(zhàn):中文字符串亂碼

JNIEXPORT jstring JNICALL Java_com_tz_jni_TestNative_chineseChars(JNIEnv * env, jobject obj,jstring jstr) {
    //java中傳入的中文->C字符串
    /*char * str = (*env)->GetStringUTFChars(env, jstr, NULL);
    FILE *fp = fopen("D://log.txt", "w");
    fputs(str, fp);
    fclose(fp);*/

    //C字符串->jstring
    char *cstr = "我說中文";
    //jstring j_str = (*env)->NewStringUTF(env, cstr);
    //借用Java的轉(zhuǎn)碼API,返回GB2312中文編碼字符串
    //使用這個構造方法,完成轉(zhuǎn)碼
    //public String(byte bytes[], String charsetName)
    //執(zhí)行這個構造方法需要三個東西
    //1.jmethodID
    //2.byte數(shù)組(jbyteArray)參數(shù)
    //3.charsetName參數(shù)(jstring)

    //String類的jclass
    jclass str_cls = (*env)->FindClass(env, "java/lang/String");
    jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");
    
    //char * -> char[] ->jbyteArray -> jbyte * 
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
    //bytes數(shù)組賦值
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);

    //charsetName="GB2312"
    jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

    //返回GB2312中文編碼jstring
    return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 一、Java 簡介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O計...
    子非魚_t_閱讀 4,538評論 1 44
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • [拳頭]堅持下去就會看見不一樣的風景,收獲更美更健康的自己! No.5 站樁第五天 2018.6.6早 站樁時間:...
    明媚堅持閱讀 190評論 0 0
  • 在老師都不知道長什么樣的情況下..... 在沒有人監(jiān)督的情況下..... 在上班的情況下..... 主動寫作業(yè).....
    yk___閱讀 158評論 0 0
  • 我要去通渭 陽光穿過無邊的蒼涼有一個聲音幸福安詳清晨我揮動白云的翅膀夜晚我匍匐在你的胸膛。生命在干旱中那么倔強,時...
    一枚冰兒閱讀 409評論 3 4

友情鏈接更多精彩內(nèi)容