Android使用JNI(從java調用本地函數)

來源:互聯網
上載者:User

Android使用JNI(從java調用本地函數)

當編寫一個混合有本地C代碼和Java的應用程式時,需要使用Java本地介面(JNI)作為串連橋樑。JNI作為一個軟體層和API,允許使用本地代碼調用Java對象的方法,同時也允許在Java方法中調用本地函數。

在Java端,開發人員所需要做的僅僅是在串連本地函數的方法之前加上native關鍵字。這樣VM就會去尋找這個本地函數。

 

1.從Java調用本地函數

從Java調用本地函數時,需要在類中定義一個帶有native關鍵字的特有方法,作為串連本地代碼的橋樑。通過這個定義,嘗試調用本地方法時JVM會找到一個名字帶有包名,類名和方法名的本地函數。

 

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數組進行排序的方法。除建構函式之外還有兩個方法。第一個是sortIntegers(),它是一個常規的Java方法,可以在其他Java類中調用它。第二個是nativeSort(),這個方法指向本地代碼中的函數。雖然可以把本地方法定義為公用的,但更好的做法是把它們作為私人方法封裝在一個Java方法中,以便進行一些錯誤處理。

 

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

 

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

 

上面命令展示了如何為之前範例程式碼中的NativeSorting產生一個標頭檔。-classpath參數指定了編譯好的類檔案位置,注意不是DEX檔案。-d參數指定了產生標頭檔的輸出目錄。運行完命令後,會在jni目錄產生com_example_liyuanjing_jniproject_NativeSorting.h檔案,它包含了本地函數的定義。

 

/* 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 __cplusplusextern "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

 

這段代碼即為產生的標頭檔。正如第一行注釋所說,不要修改這個檔案。開發人員所要做的就是把函數定義複製到實現該函數的.c檔案中。

 

下面的代碼展示了標頭檔com_example_liyuanjing_jniproject_NativeSorting.h中的JNI函數實現,本例沒有在JNI_OnLoad函數做太多的操作,只是返回了代表當前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;                    }            }    }}

 

這個樣本中,函數GetIntArrayElements,GetArrayLength和ReleaseIntArrayElements都是特定的JNI代碼。第一個函數得到一個本機資料指標,以便把資料傳給普通的C函數;第二個函數返回資料的大小;第三個函數告訴JVM本地端的工作已經完成,需要把數組複製回原地。這些函數都是必須的,因為從Java到JNI傳送複雜的資料類型時必須通過JNIEnv對象來完成。

 

注意:調用GetIntArrayElements返回一個jint指標,指向函數中jintArray裡的資料,接下來就可以把jint指標作為普通int類型指標來使用。

 

2.Android實現JNI

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

 

 

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := sorting_jni
LOCAL_SRC_FILES := array.c
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

 

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

一個Android.mk file首先必須定義好LOCAL_PATH變數。它用於在開發樹中尋找源檔案。在這個例子中,宏函數’my-dir’, 由編譯系統提供,用於返回當前路徑(即包含Android.mk file檔案的目錄)。

 

二.include $(CLEAR_VARS)

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

 

除LOCAL_PATH 。這是必要的,因為所有的編譯控制檔案都在同一個GNU MAKE執行環境中,所有的變數都是全域的。

 

三.LOCAL_MODULE := sorting_jni

LOCAL_MODULE變數必須定義,以標識你在Android.mk檔案中描述的每個模組。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的首碼和尾碼,換句話說,一個被命名為'sorting_jni'的共用庫模組,將會產生'libsorting_jni'檔案。

重要注意事項

如果你把庫命名為‘libhelloworld’,編譯系統將不會添加任何的lib首碼,也會產生libhelloworld.so,這是為了支援來源於Android平台的原始碼的Android.mk檔案,如果你確實需要這麼做的話。

 

四.LOCAL_SRC_FILES := array.c

LOCAL_SRC_FILES變數必須包含將要編譯打包進模組中的C或C++原始碼檔案。注意,你不用在這裡列出標頭檔和包含檔案,因為編譯系統將會自動為你找出依賴型的檔案;僅僅列出直接傳遞給編譯器的原始碼檔案就好。【注意,預設的C++源碼檔案的副檔名是’.cpp’. 指定一個不同的副檔名也是可能的,只要定義LOCAL_DEFAULT_CPP_EXTENSION變數,不要忘記開始的小圓點(也就是定義為‘.cxx’,而不是‘cxx’)(當然這一步我們一般不會去改它)】

 

五.include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY是編譯系統提供的變數,指向一個GNU Makefile指令碼(應該就是在build/core目錄下的shared_library.mk),負責收集自從上次調用'include $(CLEAR_VARS)'以來,定義在LOCAL_XXX變數中的所有資訊,並且決定編譯什麼,如何正確地去做。並根據其規則產生靜態庫。同理對於靜態庫。

 

當配置完上面所說的一個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

 

如果沒有意外會顯示上述正確結果。

 

然後在Android Studio項目的app/src/main/目錄下建立jinLibs目錄將產生的libs目錄中的檔案拷貝到JinLibs目錄中。如所示:

 

然後調用此方法,就可以實現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例子只是示範用的,開發人員應該使用Arrays.sort()或Collections.sort()來進行排序。通常不需要在本地進行排序,因為Java實現已經夠快了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.