Android使用NDK(從java調(diào)用本地函數(shù)'JNI')
當編寫一個混合有本地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
這段代碼即為生成的頭文件。正如第一行注釋所說,不要修改這個文件。開發(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
這個示例中,函數(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)容。