日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

java 在已有的so基础上封装jni_[干货]再见,Android JNI 封装

發布時間:2023/12/10 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 在已有的so基础上封装jni_[干货]再见,Android JNI 封装 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 1 前言

  • 2 JNI 速查表

    • 2.1 Java 和 Native 數據類型映射表

    • 2.2 引用類型

  • 3 JNI 理論基礎速覽

  • 4 JNI 常用場景示例

    • 4.1 字符串傳遞(java->native)

    • 4.2 字符串返回(native->java)

    • 4.3 數組傳遞(java->native)

    • 4.4 其他復雜對象傳遞(java->native)

    • 4.5 復雜對象返回(native->java)

    • 4.6 復雜數組對象返回(native->java)

    • 4.7 指針對象處理(nativejava)

    • 4.8 超級復雜對象操作(nativejava)

    • 4.9 靜態成員方法訪問(native->java)

    • 4.10 Context 訪問

    • 4.11 異常處理

    • 4.12 關于緩存

  • 5 JNI 庫一鍵構建框架

  • 6 封裝思路和開發工具

  • 7 后續高級擴展

  • 8 小結

  • 9 參考資料

1 前言

最近名名接到友鄰團隊的“求助”,臨時調度幫助其 SDK 封裝 JNI SDK,下面就用 SDK 和 JNI SDK 來區分這兩個 SDK。以前我也不是搞這個的,但是因為干過一兩次,多少有點經驗,之前第一次封裝后,我覺得這玩意可以總結成通用模板,但是因為本身對它不感興趣,也就沒去弄了,今天又來一次,我覺得有必要了,因為它就是個體力活。今天總結這個模板以及封裝思路,可以讓我們快速的實現 JNI 封裝。有如下這么些數據:

  • 最后分解得到基礎數據類,包括枚舉體和通用數據體類總計:40個
  • 涉及 API 個數:20個
  • 涉及 API 復雜度:
    • 有對象數組操作
    • 有指針操作
    • 最深三層類的嵌套
    • 二層嵌套和三層嵌套的類占據 1/3 左右
  • 涉及 Assets 資源文件操作

我自己挑戰了一下,花了兩天,按一天 8 小時工作量算(不包括吃飯、午睡),完成了:

  • 依賴 SDK 的熟悉,畢竟需要了解流程,在 Java 層對接口形式做適度的優化
  • Git 項目同步管理(很規矩的那種)
  • SDK 數據結構分解到 Java 類
  • 設計 Java APIs
  • 編譯框架(以前有 Native Headers 生成模板)
  • 實現 YAML 解析,我特意去找了個開源庫,并對它實現了 Bazel 工程編譯,使得 JNI SDK 直接依賴 Github 源碼(爽歪歪)
  • 去除注釋,C++ 和 Java 代碼行數 2924 行 :
    • $ find . -name "*.*" | xargs cat | grep -v -e ^$ -e ^\s*\/\/.*$ | wc -l

說了這么多,總之就是用了我的方法,兩天內輕松完成了一個 JNI SDK 的封裝。這次總結完成后,估計封裝效率又會提升一截(寫這文章前,我剛好弄完封裝任務,剩下的時間就來總結了,希望同事看到了不要告狀,不然你就看不到這篇嘔心瀝血的文章分享了)。

不來虛的,直奔重點。對了,JNI 基礎你需不需要呢?我覺得吧,看完這個,你都不用去了解 JNI 是個啥了,囫圇吞棗,依樣畫葫蘆,直接照著干,ctrl-c\ctrl-v,一梭到底(開玩笑開玩笑,多少還是要有點概念,至于這些就需要你自己去其他地方去了解了,包括 JNI 是啥、為啥要有 JNI ?、有啥利弊?、JNI Native 函數加載執行流程、JNI 靜態/動態注冊、JNI 引用、C/C++ 內存模型、Java 內存模型、JVM 內存結構、JVM GC 如何工作的等,額...)。

2 JNI 速查表

2.1 Java 和 Native 數據類型映射表

Java 類型Native 類型類型大小符號
booleanjboolean / uint8_tunsigned 8 bitsZ
bytejbyte / int8_tsigned 8 bitsB
charjchar / uint16_tunsigned 16 bitsC
shortjshort / int16_tsigned 16 bitsS
intjint / int32_tsigned 32 bitsI
longjlong / int64_tsigned 64 bitsJ
floatjfloat / float32 bitsF
doublejdouble / double64 bitsD
voidvoidN/AV
Objectjobject引用對象大小,包括 jclass/jstring/jarray/jthrowableLfully/qualified/class/name;
Stringjstring / c++對象類N/ALjava/lang/String;
Object[]jobjectArrayN/AN/A
boolean[]jbooleanArrayN/A[Z
byte[]jbyteArrayN/A[B
char[]jcharArrayN/A[C
short[]jshortArrayN/A[S
int[]jintArrayN/A[I
long[]jlongArrayN/A[J
float[]jfloatArrayN/A[F
double[]jdoubleArrayN/A[D
函數N/Apublic native long f (int n, String s, int[] arr);(argument-types)return-type,比如(ILjava/lang/String;[I)J

上面的東西你不知道,還有一個辦法,就是去自動生成的 JNI 頭文件里可以得知,想要啥,自己寫個測試函數,然后生成一下就可以知道了。

2.2 引用類型

  • jobject (all Java objects)
    • jobjectArray (object arrays)
    • jbooleanArray (boolean arrays)
    • jbyteArray (byte arrays)
    • jcharArray (char arrays)
    • jshortArray (short arrays)
    • jintArray (int arrays)
    • jlongArray (long arrays)
    • jfloatArray (float arrays)
    • jdoubleArray (double arrays)
    • jclass (java.lang.Class objects)
    • jstring (java.lang.String objects)
    • jarray (arrays)
    • jthrowable (java.lang.Throwable objects)

想更清楚的了解的朋友,可以去 jni.h 和 jni_md.h 查看。

3 JNI 理論基礎速覽

  • 「關于對象回收」:通俗點,對象只有一個,即在 Java 層 new 了,就不用在 Native 層再去 new;反之,要在 Native 層返回一個對象,則需要創建;Java 層內存是 JVM 自動管理的,Native 層,C/C++ 編寫,你懂的。
  • 「關于引用」
    • NewLocalRef:返回局部引用
    • FindClass/GetObjectClass:返回局部引用(這兩個函數作用一樣,只是傳入參數不一樣)。
    • NewObject:如果返回 Java 層繼續引用,則局部引用不會被釋放,如果是通過參數傳遞,賦值給參數,函數調用完畢就會釋放。
    • GetObjectClass:返回局部引用
    • NewCharArray:返回局部應用
    • ......
    • 傳遞給 Native 方法的每個參數,以及 JNI 函數返回的幾乎每個對象都屬于局部引用,包括 jobject 及其所有子類。
    • 局部引用僅在創建它們的線程中有效,不得將局部引用從一個線程傳遞到另一個線程。
    • jfieldID 和 jmethodID 屬于不透明類型,不是對象引用,因此總是可以緩存他們,以提升效率。而對于 jclass 就需要注意了,得使用全局引用。
    • 基本數據類型,如 int、char 之類的,在 Java 和 Native 層之間是直接拷貝一份,這個跟我們接觸的傳值、傳引用是一樣的。任何的 Java 對象都是通過引用傳遞的。
    • 「局部引用」(Local Reference): 在函數返回后會被 JVM 自動釋放掉,或者調用 (*env)->DeleteLocalRef(env, local_ref) 手動釋放(「不管怎樣」,盡量手動釋放,防止局部引用表溢出,Android 8.0 上支持無限制的局部引用)
    • 「全局引用」(Global Reference): 調用 NewGlobalRef,JVM 不會自動釋放,基于局部引用創建,可跨方法、線程使用;必須調用 (*env)->DeleteGlobalRef(env, g_ref); 手動釋放。
    • 「弱全局引用」(Weak Global Reference): 調用 NewWeakGlobalRef 基于局部引用或全局引用創建,可跨方法、線程使用;在 JVM 認為應該回收它的時候進行回收釋放,或調用 (*env)->DeleteWeakGlobalRef(env, g_ref) 手動釋放;不同上面兩種引用,不會阻止 GC 回收它引用的對象;
    • 引用比較:(*env)->IsSameObject(env, obj1_ref, obj2_ref),判斷引用對象(不分局部、全局、弱全局)是否相同。
  • 「關于類」:我們都知道類有構造函數、實例、成員方法、成員變量。
  • 「關于性能」:Native 層查找方法 ID、字段 ID、Class 引用效率是較低的(JVM 原因),因此可以基于這點在 Native 層做緩存優化。
    • FindClass()
    • GetFieldID()
    • GetMethodId()
    • GetStaticMethodID()
    • GetIntField()
  • 「關于緩存」
    • JavaVM* vm 在整個進程中唯一
    • 采用全局變量的方式緩存
    • 靜態局部變量緩存,直到程序結束才會釋放;不加鎖,多線程,存在多次緩存情況。
    • 對局部引用進行靜態變量緩存,會存在引用內容釋放,成為野指針風險
    • 全局變量緩存,聲明定義 public static native 方法,到 static {} 中調用,然后到 Native 層實現靜態方法初始化相關全局變量,也可以實現緩存
    • 返回基本類型的 Native 函數,不能造成全局引用、弱全局引用、局部引用的積累,即記得手動釋放,防止造成內存溢出
    • 返回引用類型的 Native 函數,除了要返回的引用之外,也不能造成任何的全局引用、弱全局引用、局部引用的積累
    • 對于 jmethodID 和 jfieldID 的緩存,是線程安全的。
    • jclass 需要結合 NewGlobalRef 全局引用來實現緩存。
    • jint JNI_OnLoad(JavaVM* vm, void* reserved){} 在 System.loadLibary 加載本機代碼后會自動調用;void JNI_OnUnload(JavaVM *vm, void *reserved){} 當 Classloader 銷毀后會自動調用。

4 JNI 常用場景示例

4.1 字符串傳遞(java->native)

//?public?native?CommonStatus?SetString(String?str);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetString(JNIEnv?*env,?jobject,?jstring?j_str)?{
????const?char?*c_str?=?NULL;

????c_str?=?env->GetStringUTFChars(j_str,?NULL);
????if?(NULL?==?c_str)?{
????????TEST_LOG_E("Failed?to?get?string?UTF?chars.");
????????return?getStatus(env,?FAILED);
????}
????TEST_LOG_D("c?str:?%s",?c_str);
????//?如使用?GetStringUTFRegion?與?GetStringRegion,則內部未分配內存,無需釋放
????env->ReleaseStringUTFChars(j_str,?c_str);
????return?getStatus(env,?SUCCESS);
}

4.2 字符串返回(native->java)

//?public?native?String?GetString();
JNIEXPORT?jstring?JNICALL?Java_net_xiaobaiai_test_APIs_GetString(JNIEnv?*env,?jobject)?{
????char?str[60]?=?"Hello";
????//?1.?可以用?const?char?*
????//const?char?*str?=?"Hello";
????//?2.?可以用?std::string?str?=?std::string("Hello");?str.c_str()

????jstring?result;
????result?=?env->NewStringUTF(str);
????return?result;
}

4.3 數組傳遞(java->native)

4.3.1 基本類型數組

//?public?native?CommonStatus?SetBaseTypeArray(int[]?intArray);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetBaseTypeArray(JNIEnv?*env,?jobject,?jintArray?j_array)?{
????//?step:?get?length
????int?arr_len?=?env->GetArrayLength(j_array);
????//?step:?get?array
????int?*?array?=?env->GetIntArrayElements(j_array,?NULL);
????if?(!array)?{
????????TEST_LOG_E("Failed?to?get?int?array?elements");
????????return?getStatus(env,?FAILED);
????}

????for?(int?i?=?0;?i?????????TEST_LOG_D("int?array[%d]?=?%d",?i,?array[i]);
????}
????//?也可以使用?GetIntArrayRegion/GetPrimitiveArrayCritical?區別不在展開
????env->ReleaseIntArrayElements(j_array,?array,?0);

????return?getStatus(env,?SUCCESS);
}

4.3.2 對象類型數組

//?public?native?CommonStatus?SetStringArray(String[]?strArray);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetStringArray(JNIEnv?*env,?jobject,?jobjectArray?j_str_array)?{
????//?step1:?get?array?length
????int?array_size?=?env->GetArrayLength(j_str_array);

????//?step2:?get?object?array?item?with?a?loop
????for?(int?i?=?0;?i?????????jstring?j_str?=?(jstring)(env->GetObjectArrayElement(j_str_array,?i));
????????const?char?*c_str?=?env->GetStringUTFChars(j_str,?NULL);
????????TEST_LOG_D("str?array[%d]?=?%s",?i,?c_str);

????????env->ReleaseStringUTFChars(j_str,?c_str);
????}

????return?getStatus(env,?SUCCESS);
}

4.4 其他復雜對象傳遞(java->native)

//?public?native?CommonStatus?SetPoint2DArray(Point2D[]?pointArray);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetPoint2DArray(JNIEnv?*env,?jobject,?jobjectArray?j_array)?{
????//?step1:?get?array?length
????int?array_len?=?env->GetArrayLength(j_array);
????//?step2:?get?object?array?item?with?a?loop
????for?(int?i?=?0;?i?????????//?step2.1:?get?array?element
????????jobject?j_object?=?env->GetObjectArrayElement(j_array,?i);
????????if?(!j_object)?{
????????????TEST_LOG_E("Failed?to?get?object?array?element");
????????????return?getStatus(env,?FAILED);
????????}
????????//?step2.2:?get?value
????????float?x?=?env->GetFloatField(j_object,?point2d.x);
????????float?y?=?env->GetFloatField(j_object,?point2d.y);
????????TEST_LOG_D("array[%d],?x?=?%f,?y?=?%f",?i,?x,?y);
????}
????return?getStatus(env,?SUCCESS);
}

//?public?native?CommonStatus?SetPoint(PointF?point);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetPoint(JNIEnv?*env,?jobject,?jobject?j_pointf)?{
?????//?step2.2:?get?value
????float?x?=?env->GetFloatField(j_pointf,?graphics_pointf.x);
????float?y?=?env->GetFloatField(j_pointf,?graphics_pointf.y);
????TEST_LOG_E("x?=?%f,?y?=?%f",?x,?y);
????return?getStatus(env,?SUCCESS);
}

//?public?native?CommonStatus?SetPointArrayList(ArrayList?array);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetPointArrayList(JNIEnv?*env,?jobject,?jobject?j_point_array)?{
????int?point_count?=?static_cast<int>(env->CallIntMethod(j_point_array,?array_list.size));

????if?(point_count?1)?{
????????TEST_LOG_W("The?array?size?less?than?1");
????????return?getStatus(env,?FAILED);
????}

????double?x,?y;

????for?(int?i?=?0;?i?????????jobject?point?=?env->CallObjectMethod(j_point_array,?array_list.get,?i);
????????jfloat?x?=?env->GetFloatField(point,?graphics_pointf.x);
????????jfloat?y?=?env->GetFloatField(point,?graphics_pointf.y);
????????env->DeleteLocalRef(point);

????????TEST_LOG_D("x:?%lf,?y:?%lf",?x,?y);
????}

????return?getStatus(env,?SUCCESS);
}

4.5 復雜對象返回(native->java)

//?public?native?String[]?GetStringArray(int?size);
JNIEXPORT?jobjectArray?JNICALL?Java_net_xiaobaiai_test_APIs_GetStringArray(JNIEnv?*env,?jobject,?jint?j_size)?{
????jobjectArray?result;

????result?=?(jobjectArray)env->NewObjectArray(j_size,?env->FindClass("java/lang/String"),?env->NewStringUTF(""));
????if?(!result)?{
????????TEST_LOG_E("Failed?to?new?object?array");
????????return?NULL;
????}
????for(int?i?=?0;?i??????????env->SetObjectArrayElement(result,?i,
????????????env->NewStringUTF((std::string("item?")?+?std::to_string(i)).c_str()));
????}
????return?result;
}

//?public?native?PointF?GetPointf();
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_GetPointf(JNIEnv?*env,?jobject?j_obj)?{
????//?The?generated?values?are?for?testing?only
????jobject?pt_object?=?env->NewObject(graphics_pointf.clz,
????????graphics_pointf.constructor,?j_obj,?1.22f,?3.14f);
????return?pt_object;
}

4.6 復雜數組對象返回(native->java)

4.6.1 基本類型二維數組

//?public?native?int[][]?GetInt2DArray(int?row,?int?col);
JNIEXPORT?jobjectArray?JNICALL?Java_net_xiaobaiai_test_APIs_GetInt2DArray(JNIEnv?*env,?jobject,?jint?row,?jint?col)?{
????jobjectArray?result;
????jclass?cls_int_array;
????jint?i,j;
????//?step1:?find?class
????cls_int_array?=?env->FindClass("[I");
????if?(cls_int_array?==?NULL)?{
????????return?NULL;
????}
????//?step2:?create?int?array?object
????result?=?env->NewObjectArray(row,?cls_int_array,?NULL);
????if?(result?==?NULL)?{
????????return?NULL;
????}

????//?step3:?set?value
????for?(i?=?0;?i?????????jint?buff[256];
????????jintArray?int_array?=?env->NewIntArray(col);
????????if?(int_array?==?NULL)?{
????????????return?NULL;
????????}
????????for?(j?=?0;?j?????????????buff[j]?=?i?+?j;
????????}
????????env->SetIntArrayRegion(int_array,?0,?col,?buff);
????????env->SetObjectArrayElement(result,?i,?int_array);
????????env->DeleteLocalRef(int_array);
????}

????return?result;
}

4.6.2 復雜對象數組

//?public?native?ArrayList?GetPointArrayList();
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_GetPointArrayList(JNIEnv?*env,?jobject?j_obj)?{
????const?int?array_size?=?5;
????jobject?result?=?env->NewObject(array_list.clz,?array_list.constructor,?array_size);

????for?(int?i?=?0;?i?????????//?step?1/2:?new?point
????????//?The?generated?values?are?for?testing?only
????????jobject?pt_object?=?env->NewObject(graphics_pointf.clz,?graphics_pointf.constructor,?j_obj,?0?+?i,?1?+?i);
????????//?step?2/2:?add?point?to?array?list
????????env->CallBooleanMethod(result,?array_list.add,?pt_object);
????????env->DeleteLocalRef(pt_object);
????}

????return?result;
}

4.7 指針對象處理(nativejava)

//?public?native?CommonStatus?InitHandle(PointHandle?handle);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_InitHandle(JNIEnv?*env,?jobject,?jobject?j_handle)?{
????CHandle*?handle?=?(CHandle*)malloc(sizeof(CHandle));
????if?(!handle)?{
????????TEST_LOG_E("Failed?to?new?handle.");
????????return?getStatus(env,?FAILED);
????}

????CommonStatus?status?=?InitCHandle(handle);
????if?(SUCCESS?!=?status)?{
????????TEST_LOG_E("Failed?to?init?handle?with?%d.",?status);
????????return?getStatus(env,?status);
????}
????jclass?clz_handle?=?env->GetObjectClass(j_handle);
????if?(NULL?==?clz_handle)?{
????????TEST_LOG_E("Failed?to?get?handle?object?class.");
????????return?getStatus(env,?FAILED);
????}
????jfieldID?p_handle?=?env->GetFieldID(clz_handle,?"p_handle",?"J");
????if?(NULL?==?p_handle)?{
????????TEST_LOG_E("Failed?to?get?handle?pointer.");
????????return?getStatus(env,?FAILED);
????}
????TEST_LOG_E("handle?value:?%ld",?(jlong)handle);
????env->SetLongField(j_handle,?p_handle,?(jlong)handle);

????return?getStatus(env,?SUCCESS);
}

//?public?native?CommonStatus?DestroyHandle(PointHandle?handle);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_DestroyHandle(JNIEnv?*env,?jobject,?jobject?j_handle)?{
????jlong?handle?=?getHandle(env,?j_handle);
????CHandle?*p_handle?=?(CHandle*)handle;
????if?(!p_handle)?{
????????TEST_LOG_E("Failed?to?get?handle.");
????????return?getStatus(env,?FAILED);
????}
????CommonStatus?status?=?DestroyCHandle(*p_handle);
????if?(SUCCESS?!=?status)?{
????????TEST_LOG_E("Failed?to?destroy?handle?with?%d.",?status);
????????return?getStatus(env,?FAILED);
????}

????free(p_handle);
????p_handle?=?NULL;
????return?getStatus(env,?SUCCESS);
}

4.8 超級復雜對象操作(nativejava)

//?public?native?CStruct?GetCStruct();
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_GetCStruct(JNIEnv?*env,?jobject)?{
????//?Note:?The?check?of?parameter?boundary?and?function?return?value?is?omitted?here!!!
????//?Create?Point2D
????jobject?j_point2d?=?env->NewObject(point2d.clz,?point2d.constructor);
????env->SetFloatField(j_point2d,?point2d.x,?1.1f);
????env->SetFloatField(j_point2d,?point2d.y,?1.2f);
????TEST_LOG_D("Create?Point2d?Successfully");
????//?Create?Rect
????jobject?j_rect?=?env->NewObject(rect.clz,?rect.constructor);
????env->SetIntField(j_rect,?rect.left,?1);
????env->SetIntField(j_rect,?rect.top,?2);
????env->SetIntField(j_rect,?rect.right,?3);
????env->SetIntField(j_rect,?rect.bottom,?4);
????TEST_LOG_D("Create?Rect?Successfully");
????//?Create?MyRect:?Reuse?the?point?that?have?been?created
????jobject?j_my_rect?=?env->NewObject(my_rect.clz,?my_rect.constructor);
????env->SetObjectField(j_my_rect,?my_rect.left_top,?j_point2d);
????env->SetObjectField(j_my_rect,?my_rect.right_bottom,?j_point2d);
????TEST_LOG_D("Create?MyRect?Successfully");
????//?Create?Inner?Enum
????jobject?j_inner_enum_one_obj?=?env->GetStaticObjectField(cstruct_cache_header.inner_enum_header.clz,
????????cstruct_cache_header.inner_enum_header.jid_one);
????//?Create?Inner?Class
????jobject?j_inner_class_obj?=?env->NewObject(cstruct_cache_header.innter_class_header.clz,?cstruct_cache_header.innter_class_header.constructor);
????char?c_msg[60]?=?"Hello";
????jstring?j_msg?=?env->NewStringUTF(c_msg);
????env->SetObjectField(j_inner_class_obj,?cstruct_cache_header.innter_class_header.msg,?j_msg);
????TEST_LOG_D("Create?Inner?Class?Successfully");
????//?Create?byte[]
????const?int?c_data_len?=?256;
????jbyte?c_data[c_data_len]?=?{'b',?'i',?'a',?'d',?'a',?'m',?'m'};
????jbyteArray?j_data?=?env->NewByteArray(c_data_len);
????env->SetByteArrayRegion(j_data,?0,?c_data_len,?c_data);
????TEST_LOG_D("Create?Byte?Array?Successfully");
????//?Create?2d?array
????const?int?c_double_d_array_row?=?10;
????const?int?c_double_d_array_col?=?5;
????jclass?double_d_clz?=?env->FindClass("[I");
????jobjectArray?j_double_d_array?=?env->NewObjectArray(c_double_d_array_row,?double_d_clz,?NULL);
????for?(int?i?=?0;?i?????????jintArray?j_int_array?=?env->NewIntArray(c_double_d_array_col);
????????int?c_int_array_data[c_double_d_array_col]?=?{1,?2,?3,?4,?5};
????????env->SetIntArrayRegion(j_int_array,?0,?c_double_d_array_col,?c_int_array_data);
????????env->SetObjectArrayElement(j_double_d_array,?i,?j_int_array);
????}
????TEST_LOG_D("Create?2d?Array?Successfully");
????//?Create?CStruct:?If?you?created?an?object?externally(Java?Layer),?you?don't?need?to?create?it?here.
????jobject?j_struct?=?env->NewObject(cstruct_cache_header.clz,?cstruct_cache_header.constructor);
????TEST_LOG_D("Create?CStruct?Successfully");
????//?Set?values
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_point2d,?j_point2d);
????env->SetBooleanField(j_struct,?cstruct_cache_header.jid_ztype,?JNI_FALSE);
????env->SetCharField(j_struct,?cstruct_cache_header.jid_ctype,?'Y');
????env->SetShortField(j_struct,?cstruct_cache_header.jid_stype,?8);
????env->SetIntField(j_struct,?cstruct_cache_header.jid_itype,?9);
????env->SetLongField(j_struct,?cstruct_cache_header.jid_jtype,?10);
????env->SetFloatField(j_struct,?cstruct_cache_header.jid_ftype,?11.0f);
????env->SetDoubleField(j_struct,?cstruct_cache_header.jid_dtype,?12.0);
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_data,?j_data);
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_inner_enum,?j_inner_enum_one_obj);
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_innter_class,?j_inner_class_obj);
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_rect,?j_rect);
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_myrect,?j_my_rect);
????env->SetObjectField(j_struct,?cstruct_cache_header.jid_double_d_array,?j_double_d_array);

????return?j_struct;
}

//?public?native?CommonStatus?SetCStruct(CStruct?data);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetCStruct(JNIEnv?*env,?jobject,?jobject?j_struct)?{
????if?(!j_struct)?{
????????TEST_LOG_E("Input?struct?data?is?null.");
????????return?getStatus(env,?FAILED);
????}
????//?Note:?The?check?of?parameter?boundary?and?function?return?value?is?omitted?here!!!
????//?Get?byte[]?data
????jbyteArray?j_data_array?=?(jbyteArray)env->GetObjectField(j_struct,?cstruct_cache_header.jid_data);
????if?(NULL?==?j_data_array)?{
????????TEST_LOG_E("Failed?to?get?object?field.");
????????return?getStatus(env,?FAILED);
????}
????jbyte?*c_data?=?env->GetByteArrayElements(j_data_array,?JNI_FALSE);
????//?Get?basic?type?value
????jboolean?c_ztype?=?env->GetBooleanField(j_struct,?cstruct_cache_header.jid_ztype);
????jchar?c_ctype?=?env->GetCharField(j_struct,?cstruct_cache_header.jid_ctype);
????jshort?c_stype?=?env->GetShortField(j_struct,?cstruct_cache_header.jid_stype);
????jint?c_itype?=?env->GetIntField(j_struct,?cstruct_cache_header.jid_itype);
????jlong?c_jtype?=?env->GetLongField(j_struct,?cstruct_cache_header.jid_jtype);
????jfloat?c_ftype?=?env->GetFloatField(j_struct,?cstruct_cache_header.jid_ftype);
????jdouble?c_dtype?=?env->GetDoubleField(j_struct,?cstruct_cache_header.jid_dtype);
????TEST_LOG_E("Get?basic?type?value?successfully");
????//?Get?Point2D?value
????jobject?j_point2d?=?env->GetObjectField(j_struct,?cstruct_cache_header.jid_point2d);
????jfloat?c_point2d_x?=?env->GetFloatField(j_point2d,?point2d.x);
????jfloat?c_point2d_y?=?env->GetFloatField(j_point2d,?point2d.y);
????TEST_LOG_E("Get?Point2D?value?successfully");
????//?Get?Rect?value
????jobject?j_rect?=?env->GetObjectField(j_struct,?cstruct_cache_header.jid_rect);
????jint?c_rect_left?=?env->GetIntField(j_rect,?rect.left);
????jint?c_rect_top?=?env->GetIntField(j_rect,?rect.top);
????jint?c_rect_right?=?env->GetIntField(j_rect,?rect.right);
????jint?c_rect_bottom?=?env->GetIntField(j_rect,?rect.bottom);
????TEST_LOG_E("Get?Rect?value?successfully");
????//?Get?MyRect?value
????jobject?j_my_rect?=?env->GetObjectField(j_struct,?cstruct_cache_header.jid_myrect);
????jobject?j_my_rect_point2d_lefttop?=?env->GetObjectField(j_my_rect,?my_rect.left_top);
????jobject?j_my_rect_point2d_rightbottom?=?env->GetObjectField(j_my_rect,?my_rect.right_bottom);
????jfloat?c_my_rect_lefttop_x?=?env->GetFloatField(j_my_rect_point2d_lefttop,?point2d.x);
????jfloat?c_my_rect_lefttop_y?=?env->GetFloatField(j_my_rect_point2d_rightbottom,?point2d.y);
????jfloat?c_my_rect_rightbottom_x?=?env->GetFloatField(j_my_rect_point2d_rightbottom,?point2d.x);
????jfloat?c_my_rect_rightbottom_y?=?env->GetFloatField(j_my_rect_point2d_rightbottom,?point2d.y);
????TEST_LOG_E("Get?MyRect?value?successfully");
????//?Get?inner?enum?type
????jint?img_format_value?=?env->CallIntMethod(j_struct,?cstruct_cache_header.inner_enum_md);
????TEST_LOG_E("Get?Inner?enum?value?successfully");
????//?Get?inner?class?type
????jobject?j_inner_class?=?env->GetObjectField(j_struct,?cstruct_cache_header.jid_innter_class);
????jstring?j_inner_class_msg?=?(jstring)env->GetObjectField(j_inner_class,?cstruct_cache_header.innter_class_header.msg);

????const?char?*c_str?=?env->GetStringUTFChars(j_inner_class_msg,?NULL);
????if?(NULL?==?c_str)?{
????????TEST_LOG_E("Failed?to?get?string?UTF?chars.");
????????return?getStatus(env,?FAILED);
????}
????TEST_LOG_D("c?str:?%s",?c_str);
????//?Release?byte[]
????env->ReleaseByteArrayElements(j_data_array,?c_data,?0);
????return?getStatus(env,?SUCCESS);
}

4.9 靜態成員方法訪問(native->java)

//?public?native?void?CallStaticMethod();
JNIEXPORT?void?JNICALL?Java_net_xiaobaiai_test_APIs_CallStaticMethod(JNIEnv?*env,?jobject)?{
????jstring?j_str?=?env->NewStringUTF("Hello?Static?Method");
????env->CallStaticVoidMethod(static_method_header.clz,?static_method_header.static_md,?j_str,?100);

????env->DeleteLocalRef(j_str);
}

4.10 Context 訪問

//?public?native?CommonStatus?SetContext(Context?context);
JNIEXPORT?jobject?JNICALL?Java_net_xiaobaiai_test_APIs_SetContext(JNIEnv?*env,?jobject,?jobject?context)?{
????jclass?context_clz?=?env->GetObjectClass(context);

????//?get?android?application?package?name
????jmethodID?m_getpackagename_id?=?env->GetMethodID(context_clz,?"getPackageName",?"()Ljava/lang/String;");
????if?(!context_clz?||?!m_getpackagename_id)?{
????????TEST_LOG_E("Failed?to?get?class?or?method?id");
????????return?getStatus(env,?FAILED);
????}

????jstring?j_pkg_name?=?static_cast(env->CallObjectMethod(context,?m_getpackagename_id));if?(!j_pkg_name)?{
????????TEST_LOG_E("Failed?to?call?object?method.");return?getStatus(env,?FAILED);
????}const?char*?pkg_name?=?env->GetStringUTFChars(j_pkg_name,?0);if?(NULL?==?pkg_name)?{
????????TEST_LOG_E("Failed?to?get?string?UTF?chars.");return?getStatus(env,?FAILED);
????}
????TEST_LOG_D("package?name?=?%s",?pkg_name);
????env->ReleaseStringUTFChars(j_pkg_name,?pkg_name);return?getStatus(env,?SUCCESS);
}

4.11 異常處理

  • env->ExceptionOccurred
  • env->ExceptionClear

在 native 層處理異常,這里就不展開了。

4.12 關于緩存

通過 jint JNI_OnLoad(JavaVM* vm, void* reserved 實現緩存初始化,void JNI_OnUnload(JavaVM *vm, void *reserved 實現緩存釋放。緩存主要實現對 jclass、jfieldID、jmethodID 的緩存,具體可以參見:https://github.com/yicm/BazelMixedLanguage 代碼實現。

5 JNI 庫一鍵構建框架

  • 支持 JNI Native 頭文件自動生成
  • 支持 JNI Library 生成
    • 支持端到端,頭文件生成->JNI 庫生成
  • 支持 Android APP 命令行編譯(測試 JNI Library)
    • 支持端到端,頭文件生成->JNI 庫生成->APK 生成

如果你很熟悉 JNI 的 Native 函數命名規則,可以直接根據 Java 類手擼 Native 層函數原型命名(我干不了)。有了這個框架,編譯這一塊也搞定了,效率杠杠的,具體可以參見開源項目:https://github.com/yicm/BazelMixedLanguage

6 封裝思路和開發工具

思路:

  • SDK 頭文件拿到一梭子基本按照其數據結構類型編寫 Java 層數據結構類型
  • 設計 Java 層 APIs(需要了解 SDK 調用方式和輸入輸出)
  • 直接利用框架開始編碼、編譯、調試、測試
  • 涉及到開發工具如下:

    • Vim + VS Code:代碼編輯
    • Bazel:編譯
    • adb:程序安裝和調試
    • Android Studio:輔助創建工程和代碼編輯(可選)

    7 后續高級擴展

    • JNA
      • https://github.com/java-native-access/jna
    • 一個想法
      • 直接通過 C 庫頭文件生成 Java 代碼,效率再次提升,就不折騰了。

    8 小結

    文章內容有點多,如果完整的啃下來,JNI 這塊就可以說 88 了。

    本文涉及到了 JNI 封裝中常用的模版,嘔心瀝血,看完這么多模板,你都能發現其中規律了,就那么幾個操作。但是畢竟我只封裝了三次,歡迎補充和指正,一起來提高開發效率。另外歡迎嘗試 https://github.com/yicm/BazelMixedLanguage,其中包含 Java 層代碼,該開源項目絕對不會讓你失望的。

    9 參考資料

    • https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
    • https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html#wp9502
    • https://developer.android.google.cn/studio/profile/memory-profiler#jni-references
    • https://developer.android.google.cn/training/articles/perf-jni
    • https://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/performance.html
    • https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#referencing_java_objects
    • https://www.kancloud.cn/owenoranba/jni/120442

    總結

    以上是生活随笔為你收集整理的java 在已有的so基础上封装jni_[干货]再见,Android JNI 封装的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。