Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android使用JNI(從java調用本地函數)

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

編輯:關於Android編程

當編寫一個混合有本地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 __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

 

這段代碼即為生成的頭文件。正如第一行注釋所說,不要修改這個文件。開發者所要做的就是把函數定義復制到實現該函數的.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實現已經夠快了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved