Android使用NDK(從java調(diào)用本地函數(shù)'JNI')

      網(wǎng)友投稿 1046 2025-04-01

      當編寫一個混合有本地C代碼和Java的應用程序時,需要使用Java本地接口(JNI)作為連接橋梁。JNI作為一個軟件層和API,允許使用本地代碼調(diào)用Java對象的方法,同時也允許在Java方法中調(diào)用本地函數(shù)。


      在Java端,開發(fā)者所需要做的僅僅是在連接本地函數(shù)的方法之前加上native關(guān)鍵字。這樣VM就會去尋找這個本地函數(shù)。

      1.從Java調(diào)用本地函數(shù)

      從Java調(diào)用本地函數(shù)時,需要在類中定義一個帶有native關(guān)鍵字的特有方法,作為連接本地代碼的橋梁。通過這個定義,嘗試調(diào)用本地方法時JVM會找到一個名字帶有包名,類名和方法名的本地函數(shù)。

      package com.example.liyuanjing.jniproject; import android.util.Log; public class NativeSorting { static { System.loadLibrary("sorting_jni"); } public NativeSorting() { } public void sortIntegers(int[] ints) { nativeSort(ints); for (int i = 0; i < ints.length-1; i++) { System.out.print(String.valueOf(ints[i])); Log.i("liyuanjinglyj",String.valueOf(ints[i])); } } private native void nativeSort(int[] ints); }

      上面是一個簡化的示例,包括一個對int數(shù)組進行排序的方法。除構(gòu)造函數(shù)之外還有兩個方法。第一個是sortIntegers(),它是一個常規(guī)的Java方法,可以在其他Java類中調(diào)用它。第二個是nativeSort(),這個方法指向本地代碼中的函數(shù)。雖然可以把本地方法定義為公共的,但更好的做法是把它們作為私有方法包裝在一個Java方法中,以便進行一些錯誤處理。

      可以從頭開始寫本地代碼,但也可以借助javah工具來生成部分代碼,該工具在Java SDK中。它會生成一個C語言頭文件,包括本地方法對應的函數(shù)定義。首先要編譯Java程序代碼,然后在當前項目的src/main目錄運行如下命令:

      javah -classpath ../../build/intermediates/classes/debug/ -d jni/ com.example.liyuanjing.jniproject.NativeSorting

      上面命令展示了如何為之前示例代碼中的NativeSorting生成一個頭文件。-classpath參數(shù)指定了編譯好的類文件位置,注意不是DEX文件。-d參數(shù)指定了生成頭文件的輸出目錄。運行完命令后,會在jni目錄生成com_example_liyuanjing_jniproject_NativeSorting.h文件,它包含了本地函數(shù)的定義。

      /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_example_liyuanjing_jniproject_NativeSorting */ #ifndef _Included_com_example_liyuanjing_jniproject_NativeSorting #define _Included_com_example_liyuanjing_jniproject_NativeSorting #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_liyuanjing_jniproject_NativeSorting * Method: nativeSort * Signature: ([I)V */ JNIEXPORT void JNICALL Java_com_example_liyuanjing_jniproject_NativeSorting_nativeSort (JNIEnv *, jobject, jintArray); #ifdef __cplusplus } #endif #endif

      這段代碼即為生成的頭文件。正如第一行注釋所說,不要修改這個文件。開發(fā)者所要做的就是把函數(shù)定義復制到實現(xiàn)該函數(shù)的.c文件中。

      下面的代碼展示了頭文件com_example_liyuanjing_jniproject_NativeSorting.h中的JNI函數(shù)實現(xiàn),本例沒有在JNI_OnLoad函數(shù)做太多的操作,只是返回了代表當前JNI版本為1.6的常量,這是Dalvik VM支持的一個版本,下面是array.c代碼:

      #include #include #include "com_example_liyuanjing_jniproject_NativeSorting.h" void quicksort(int *arr, int start, int end); JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_VERSION_1_6; } JNIEXPORT void JNICALL Java_com_example_liyuanjing_jniproject_NativeSorting_nativeSort (JNIEnv *env, jobject obj, jintArray data) { jint* array = (*env)->GetIntArrayElements(env, data, 0); jint length = (*env)->GetArrayLength(env, data); quicksort(array, 0, length); (*env)->ReleaseIntArrayElements(env, data, array, 0); } void quicksort(int *arr, int start, int end) { int i, j, temp; for (i = 0; i < end-1; i++) { for (j = 0; j < end - i-1; j++) { if (*(arr+j) < *(arr+j+1)) { temp = *(arr+j); *(arr+j) = *(arr+j+1); *(arr+j+1) = temp; } } } }

      Android使用NDK(從java調(diào)用本地函數(shù)'JNI')

      這個示例中,函數(shù)GetIntArrayElements,GetArrayLength和ReleaseIntArrayElements都是特定的JNI代碼。第一個函數(shù)得到一個本地數(shù)據(jù)指針,以便把數(shù)據(jù)傳給普通的C函數(shù);第二個函數(shù)返回數(shù)據(jù)的大小;第三個函數(shù)告訴JVM本地端的工作已經(jīng)完成,需要把數(shù)組復制回原地。這些函數(shù)都是必須的,因為從Java到JNI傳送復雜的數(shù)據(jù)類型時必須通過JNIEnv對象來完成。

      注意:調(diào)用GetIntArrayElements返回一個jint指針,指向函數(shù)中jintArray里的數(shù)據(jù),接下來就可以把jint指針作為普通int類型指針來使用。

      2.Android實現(xiàn)JNI

      要想Android能運行起來,必須到NDK目錄android-ndk-r10d\samples\native-activity\jni目錄下拷貝Android.mk,到剛才放置com_example_liyuanjing_jniproject_NativeSorting.h和array.c同一目錄下,當然還要更改Android.mk的幾個值。

      $(call import-module,android/native_app_glue)

      一.LOCAL_PATH := $(call my-dir)

      一個Android.mk file首先必須定義好LOCAL_PATH變量。它用于在開發(fā)樹中查找源文件。在這個例子中,宏函數(shù)’my-dir’,?由編譯系統(tǒng)提供,用于返回當前路徑(即包含Android.mk file文件的目錄)。

      二.include $(CLEAR_VARS)

      CLEAR_VARS由編譯系統(tǒng)提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變量(例如?LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES,?等等...),

      除LOCAL_PATH?。這是必要的,因為所有的編譯控制文件都在同一個GNU MAKE執(zhí)行環(huán)境中,所有的變量都是全局的。

      三.LOCAL_MODULE ? ?:= sorting_jni

      LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統(tǒng)會自動產(chǎn)生合適的前綴和后綴,換句話說,一個被命名為'sorting_jni'的共享庫模塊,將會生成'libsorting_jni'文件。

      重要注意事項

      如果你把庫命名為‘libhelloworld’,編譯系統(tǒng)將不會添加任何的lib前綴,也會生成libhelloworld.so,這是為了支持來源于Android平臺的源代碼的Android.mk文件,如果你確實需要這么做的話。

      四.LOCAL_SRC_FILES := array.c

      LOCAL_SRC_FILES變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,你不用在這里列出頭文件和包含文件,因為編譯系統(tǒng)將會自動為你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。【注意,默認的C++源碼文件的擴展名是’.cpp’.?指定一個不同的擴展名也是可能的,只要定義LOCAL_DEFAULT_CPP_EXTENSION變量,不要忘記開始的小圓點(也就是定義為‘.cxx’,而不是‘cxx’)(當然這一步我們一般不會去改它)】

      五.include $(BUILD_SHARED_LIBRARY)

      BUILD_SHARED_LIBRARY是編譯系統(tǒng)提供的變量,指向一個GNU Makefile腳本(應該就是在build/core目錄下的shared_library.mk),負責收集自從上次調(diào)用'include $(CLEAR_VARS)'以來,定義在LOCAL_XXX變量中的所有信息,并且決定編譯什么,如何正確地去做。并根據(jù)其規(guī)則生成靜態(tài)庫。同理對于靜態(tài)庫。

      當配置完上面所說的一個C頭文件,一個.c文件,一個Android.mk文件后,進入CMD到當前目錄中。輸入ndk-build命令:

      [armeabi] Compile thumb ?: sorting_jni <= array.c

      [armeabi] SharedLibrary ?: libsorting_jni.so

      [armeabi] Install ? ? ? ?: libsorting_jni.so => libs/armeabi/libsorting_jni.so

      如果沒有意外會顯示上述正確結(jié)果。

      然后在Android Studio項目的app/src/main/目錄下建立jinLibs目錄將生成的libs目錄中的文件拷貝到JinLibs目錄中。如下圖所示:

      然后調(diào)用此方法,就可以實現(xiàn)Android使用JNI的功能了。

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 9

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 8

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 7

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 6

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 5

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 4

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 3

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 2

      05-25 20:02:46.720 ?32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 1

      之前的JNI例子只是演示用的,開發(fā)者應該使用Arrays.sort()或Collections.sort()來進行排序。通常不需要在本地進行排序,因為Java實現(xiàn)已經(jīng)夠快了。

      Android Java

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:企業(yè)投資于員工敬業(yè)度的重要性
      下一篇:5G 的關(guān)鍵技術(shù)筆記分享——三大關(guān)鍵革新
      相關(guān)文章
      国产区图片区小说区亚洲区| 老牛精品亚洲成av人片| 午夜亚洲av永久无码精品| 亚洲一区二区三区高清在线观看| 亚洲综合激情视频| 亚洲成a人片在线观| 亚洲同性男gay网站在线观看| 亚洲精品午夜视频| 亚洲电影免费观看| 亚洲欧洲日韩综合| 亚洲国产日韩在线成人蜜芽 | 午夜亚洲www湿好大| 亚洲AV日韩AV永久无码绿巨人| 久久国产精品亚洲一区二区| 国产亚洲一区二区在线观看 | 亚洲自偷自偷图片| 亚洲熟妇av一区二区三区| 亚洲精品无码不卡在线播放HE| 国产亚洲精品a在线无码| 亚洲av午夜成人片精品网站| 狠狠色伊人亚洲综合成人| 亚洲第一精品在线视频| 亚洲黄色免费电影| 亚洲六月丁香六月婷婷蜜芽| 亚洲娇小性色xxxx| 亚洲Aⅴ在线无码播放毛片一线天| 爱情岛论坛亚洲品质自拍视频网站| 国产亚洲Av综合人人澡精品| 亚洲国产一区视频| 亚洲中文字幕在线乱码| 亚洲阿v天堂在线| 亚洲AV日韩精品久久久久久久| 久久亚洲精品成人AV| 亚洲精品美女在线观看播放| 亚洲丰满熟女一区二区v| 亚洲精品色播一区二区| 亚洲国产精品激情在线观看| 中文字幕亚洲一区二区三区| 亚洲成AV人片在线观看无码| 亚洲视频在线观看视频| 国产亚洲精品VA片在线播放|