Android JNI開發入門之二

來源:互聯網
上載者:User

  在上一篇文章《Android JNI開發入門之一》中,我介紹了Android應用程式(APK)怎樣通過JNI調用Native C實現的共用庫。本文將進一步介紹Android應用程式通過JNI調用Native C++實現的共用庫,並實現一個和上文《Android JNI開發入門之一》相同功能的Helloworld應用程式。

兩套不同的API

   
前文已經提到,Android系統的Java虛擬機器為C和C++實現兩套不同的API,所以我們調用的時候需要注意這一點兒。另外Google並沒有提供
JNI的文檔,我們調用的時候可以參考Android的jni.h檔案,裡面有C和C++的JNI函數原型。也可以把本例的相同功能HelloWorld
庫和上文《Android JNI開發入門之一》進行比較。

C++實現HelloWorld共用庫

    在本例中Android應用程式不需要有任何變化,我們需要重新用C++實現HelloWorld共用庫。建立com_simon_Helloworld.cpp檔案,並在檔案中輸入如下內容:

#include <jni.h>#define LOG_TAG "HelloWorld"#include <utils/Log.h> /*  * Class:     com_simon_Helloworld  * Method:    print  * Signature: ()V  *//*JNIEXPORT void JNICALL Java_com_simon_Helloworld_print(JNIEnv *, jobject)*/JNIEXPORT jstring JNICALL Java_com_simon_HelloWorld_printJNI(JNIEnv *env, jobject obj){    LOGI("Hello World From libhelloworld.so!");return env->NewStringUTF("Hello World!");}static const char *classPathName = "com/simon/HelloWorld";static JNINativeMethod methods[] = {  {"printJNI", "()Ljava/lang/String;", (void*)Java_com_simon_HelloWorld_printJNI },};/* * Register several native methods for one class. */static int registerNativeMethods(JNIEnv* env, const char* className,    JNINativeMethod* gMethods, int numMethods){    jclass clazz;    clazz = env->FindClass(className);    if (clazz == NULL) {        LOGE("Native registration unable to find class '%s'", className);        return JNI_FALSE;    }    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        LOGE("RegisterNatives failed for '%s'", className);        return JNI_FALSE;    }    return JNI_TRUE;}/* * Register native methods for all classes we know about. * * returns JNI_TRUE on success. */static int registerNatives(JNIEnv* env){  if (!registerNativeMethods(env, classPathName,                 methods, sizeof(methods) / sizeof(methods[0]))) {    return JNI_FALSE;  }  return JNI_TRUE;}typedef union {    JNIEnv* env;    void* venv;} UnionJNIEnvToVoid;/* This function will be call when the library first be loaded */jint JNI_OnLoad(JavaVM* vm, void* reserved){    UnionJNIEnvToVoid uenv;    JNIEnv* env = NULL;    LOGI("JNI_OnLoad!");    if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {        LOGE("ERROR: GetEnv failed");        return -1;    }    env = uenv.env;;    if (registerNatives(env) != JNI_TRUE) {        LOGE("ERROR: registerNatives failed");        return -1;    }     return JNI_VERSION_1_4;}

本例與上文《Android JNI開發入門之一》對比有如下幾點不同需要注意:

1、C和C++實現共用庫調用不同JNI API。前面已經提到Android系統JNI為C和C++提供了兩套不同的API。請仔細對比NewStringUTF,GetEnv函數,就會發現JNI API不同。

2、C++版的helloworld共用庫提供了函數映射表。前文《

Android JNI開發入門之一》也已經提到,JNI API為了避免醜陋的函數名,提供了方法向Java虛擬機器註冊函數映射表。這樣當Java調用Native介面的時候,Java虛擬機器就可以不用根據函數名來決定調用哪個函數了,直接通過查詢表格就可以找到需要調用的函數了。

3、我們注意到RegisterNatives第一個參數(C語言介面中是第二個參數)為調用該函數的Java類。這也和標準JNI函數名包含類名(包名和類名)的作用一樣——聲明那個Java類可以調用這個方法。

4、函數映射表的定義非常的怪異。你可以參考

Android JNI 使用的資料結構JNINativeMethod詳解和JNI標準手冊相互關聯類型的部分。

   
通過對比你會發現C++的實現同樣功能的共用庫比C加入更多的代碼,另外你可能會有疑問既然Java虛擬機器能用通過函數名訪問到相應的Native
code函數,為什麼還要提供註冊映射函數表呢?沒錯!作為一個HelloWorld程式,確實簡單為第一要務!如果Java虛擬機器能用函數名能訪問到相
應的函數的話,我是不會多此一舉來註冊映射函數表。在實踐中我發現:

   標準JNI不能通過標準函數名找到C++實現的Helloworld共用庫中的函數,但是C實現的helloworld共用沒有這個問題。我不知道為什麼會這樣,請達人指教。沒有辦法才提供註冊映射函數表。

    下面提供一個helloworld共用庫的Makefile——Android.mk:

LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:=com_simon_Helloworld.cppLOCAL_C_INCLUDES := $(JNI_H_INCLUDE)LOCAL_MODULE := libhelloworldLOCAL_SHARED_LIBRARIES := libutilsLOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)

    和前文《Android JNI開發入門之一》的Android.mk檔案相比也就是修改了一下源檔案,沒有什麼可說。編譯產生libhelloworld.so檔案,允許前文HelloWorld Android應用程式,你將會得到和前文相同的結果。

JNI的進一步學習

    通過上面的例子我們已經初步掌握了Android編寫JNI程式的方法。這也只能算是Android JNI入門而已,有很多JNI相關的內容我們在例子中並沒有涉及到,比如:

1、我們並沒有提到怎樣在Native代碼中回調Java的函數。

2、每個Native的函數中前兩個參數是什麼意思?

    如果想用JNI進行Android應用開發我們需要更深入的學習。為了大家共同進步,我這裡也可以提供一些相關的資料和方法。

    如果我們想深入學習JNI首先要先熟悉標準的JNI。推薦大家學習《Java Native Interface: Programmer’s Guide and Specification》,權威的標準JNI學習文檔。

    前文提到Google沒有Android JNI編程提供文檔,但是在網上Simon也找到了一篇非常好的文檔供大家參考——JNI Examples for Android。這篇文章中舉了一個例子包含了Android JNI開發的方方面面,想深入學習Android JNI開發的朋友請仔細研讀。

總結

    俗話說得好“問道有先後,如是而已”,Simon也是剛剛才開始學習Android JNI編程,對JNI的理解上面難免有一些地方有錯誤,歡迎朋友們指正。

參考資料:

Android JNI 使用的資料結構JNINativeMethod詳解

Android JNI執行個體

JNI Examples for Android

How to add a new module to Android

Android JNI(實現自己的JNI_OnLoad函數)

Android中JNI編程的那些事兒

Java Native Interface: Programmer’s Guide and Specification

Java Native Interface Specification

原文

相關文章

聯繫我們

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