Android開發之JNI調用本地C庫專題(一):JNI的使用

來源:互聯網
上載者:User

標籤:android開發jni   jni開發   ndk開發   native方法   java調用c   

JNI,是用於開發本地C函數庫的技術。用於連結JAVA和C或者C++語言的橋樑。在部分android項目開發中,我們是需要用到這項技術的。那麼廢話不多說,進入正題。

開發JNI,需要用到NDK,這個大家應該都知道了。還需要一個linux的開發環境。一般而言,可以使用虛擬機器裝一個ubantu,博主以前就是搞linux開發的,這點還是比較熟悉。但是對於大部分android開發人員而言,弄一個虛擬機器成本太高。那麼,我們需要搭建一個類比linux的開發環境。這個博主就不說了,直接上連結

NDK環境搭建

以上博文其實只需要做完第三步即可,如果是下載安裝Google官方整合的eclipse,第三步都可以不用做了。

好,當一切東西都準備好了之後,我們以一個例子來講解如何開發一個JNI項目。


一、建立一個Android項目這個正常使用,就是建立一個Android項目

二、C語言方法實現。1、建立本地native方法一般而言,需要用C語言實現的方法,我們需要用native關鍵字去修飾,這些方法可以放在任何一個類中,博主為方便,就都放入一個類中去。參考代碼:
public class DataProvider {public native int add(int x, int y); //public native String sayHelloInC(String s);public native int[] intMethod(int[] iNum);}

2、編譯native方法這裡需要用C語言去實現三個方法,一般而言,我們用到JNI技術,都是用做加密。所以,上述三個方法應該是常用的方法。這個使用,我們需要將這個類用javah去編譯產生C代碼的標頭檔。首先,我們得在CMD視窗中進入到android項目中的src檔案夾中(如果是JDK1.6,則需要進入到/bin/classes目錄中,博主的是JDK1.7,所以進入的是src目錄),然後執行 javah com.example.ndkpassdata.DataProvider(這裡需要用到全路徑全類名)

然後我們重新整理一下項目,會發現在src目錄下產生了一個.h標頭檔,:
3、建立JNI目錄在工程中建立一個名字為jni的檔案夾,名字千萬不要弄錯,
將剛剛的標頭檔,copy到該檔案夾下,然後建立一個名字一樣的.c檔案。點開 標頭檔後,我們發現剛剛寫的三個方法都已經產生,:

4、編寫.c檔案這個時候,我們需要在建立的.c檔案中,寫入這三個方法。.c檔案如何寫,相信會C語言的同學應該都明白,關於c代碼中如何轉換java傳入過來的參數和調用java中的方法,可以參考jni.h的標頭檔,裡面有詳細介面調用方法。這裡博主就不在描述,直接上所有的代碼,有詳細注釋:
#include <stdio.h>#include "com_example_ndkpassdata_DataProvider.h"#include <android/log.h>#include <string.h>#define LOG_TAG "clog"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)//將java語言中的字串格式轉換為C語言中的字串格式。char* Jstring2CStr(JNIEnv* env, jstring jstr) {char* rtn = NULL;jclass clsstring = (*env)->FindClass(env, "java/lang/String");jstring strencode = (*env)->NewStringUTF(env, "GB2312");jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes","(Ljava/lang/String;)[B");jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,strencode); // String .getByte("GB2312");jsize alen = (*env)->GetArrayLength(env, barr);jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);if (alen > 0) {rtn = (char*) malloc(alen + 1);         //"\0"memcpy(rtn, ba, alen);rtn[alen] = 0;}(*env)->ReleaseByteArrayElements(env, barr, ba, 0);  //return rtn;}JNIEXPORT jint JNICALL Java_com_example_ndkpassdata_DataProvider_add(JNIEnv * env, jobject jobject, jint x, jint y) {// 想在logcat控制台上 列印日誌LOGD("x=%d", x);LOGI("y=%d", y);// log.i(TAG,"sss");return x + y;}JNIEXPORT jstring JNICALL Java_com_example_ndkpassdata_DataProvider_sayHelloInC(JNIEnv * env, jobject jobject, jstring str) {char* c = "hello";// 在C語言中不能直接操作java中的字串// 把java中的字串轉換成c語言中 char數組char* cstr = Jstring2CStr(env, str);strcat(cstr, c);LOGD("%s", cstr);return (*env)->NewStringUTF(env, cstr);}JNIEXPORT jintArray JNICALL Java_com_example_ndkpassdata_DataProvider_intMethod(JNIEnv * env, jobject jobject, jintArray jarray) {// jArray  遍曆數組   jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);// 數組的長度    jsize       (*GetArrayLength)(JNIEnv*, jarray);// 對數組中每個元素 +5int length = (*env)->GetArrayLength(env, jarray);//拿到指標初始位置int* array = (*env)->GetIntArrayElements(env, jarray, 0);int i = 0;for (; i < length; i++) {*(array + i) += 5;}return jarray;}

5、編寫android.mk檔案該檔案的寫法請到ndk目錄下的docs目錄,開啟ANDROID-MK.html,裡面有使用方法說。這裡博主就說說一般必須寫的。
LOCAL_PATH := $(call my-dir)   // 返回當前c代碼目錄
include $(CLEAR_VARS)        // 清楚了所有 已local 開頭的設定檔 唯獨不清楚LOCAL_PATH
LOCAL_MODULE    := hello   // 庫函數的名字  嚴格遵守makefile 格式  lib  .so  如果前面加lib 不會自動產生了
   LOCAL_SRC_FILES := Hello.c  //源檔案名稱,就是剛剛建立的那個.c檔案的名稱。
include $(BUILD_SHARED_LIBRARY)  // 加入庫函數由於在之前的代碼中使用了C語言的日誌函數log,所以在.mk檔案中需要加入庫引用的聲明,代碼如下:
 LOCAL_PATH := $(call my-dir)   include $(CLEAR_VARS)   LOCAL_MODULE    := libhello   LOCAL_SRC_FILES := Hello.cLOCAL_LDLIBS += -llog   include $(BUILD_SHARED_LIBRARY)

6、編譯C檔案當android檔案寫好之後,一切的準備工作都已經就緒,這個時候我們只需要調用NDK去編譯該項目即可上次.so動態庫檔案。這個時候需要啟動Cygwin,然後來到該項目的目錄下,調用ndk-build命令即可。過程:

當編譯完成之後我們重新整理項目,會發現多出了一個obj檔案夾,在libs檔案夾中也會多出一個armeabs檔案夾,在這裡面就有我們剛剛編譯產生的庫。
三、C語言函數庫方法調用那麼編譯好之後,我們需要調用剛剛的方法,這個時候就簡單了。在調用的地方,需要加入一個靜態代碼塊,利用System.loadLibrary("hello");方法將剛剛生產的庫檔案匯入。詳細看代碼:
public class MainActivity extends Activity {DataProvider provider;static {System.loadLibrary("hello");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);provider = new DataProvider();}public void click1(View view) {int result = provider.add(6, 8);System.out.println(result);}public void click2(View view) {String str = provider.sayHelloInC("freedom");Toast.makeText(getApplicationContext(), str, 0).show();}public void click3(View view) {int[] arr = new int[] { 5, 6, 7, 8, 9 };provider.intMethod(arr);for (int i : arr) {System.out.println(i);}}}

好了,至此利用JNI開發本地native方法的流程已經講解完畢,需要值得注意的是,如果本地.c檔案有變更,我們需要調用ndk-build去重新編譯.c檔案,這個時候最好將本機快取目錄obj檔案夾刪除掉。希望能協助到看到此文的人。

Android開發之JNI調用本地C庫專題(一):JNI的使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.