1. 程式人生 > >JNI和NDK程式設計知識點

JNI和NDK程式設計知識點

總結歸納JNI和NDK相關的知識點。

JNI和NDK程式設計

版本:2018/3/18-1(11:36

JNI和NDK簡介

1、JNI是什麼?作用?

  1. Java Native Interface(java本地介面)
  2. 方便Java呼叫C、C++等原生代碼所封裝的一層介面

2、為什麼需要JNI?

  1. java特點是跨平臺,但是會導致本地互動能力不夠強大,一些作業系統相關特性Java是無法完成的-因此Java提供JNI專門用於和原生代碼互動,依次增強本地互動能力
  2. 可以使用現有的開源庫,現在很多優秀的開源庫都是用 C/C++ 編寫的。
  3. 程式碼的保護,Android apk 的 java 程式碼容易被反編譯,而 C/C++ 更難反編譯。
  4. 便於移植,用 C/C++ 寫的庫可以方便在其他嵌入式平臺使用。

3、NDK是什麼?

  1. NDK是Android所提供的工具集合
  2. 通過NDK可以在Android中更加方便地通過JNI來訪問原生代碼,如C\C++
  3. NDK還提供交叉編譯器,只需要簡單地修改mk檔案就可以生成特定CPU平臺的動態庫

4、NDK的優點

  1. 提高程式碼的安全性—so庫反編譯比較困難
  2. 可以方便地使用目前已有的C/C++開源庫
  3. 便於平臺間的移植—通過C/C++實現的動態庫可以很方便在其他平臺上使用
  4. 提高程式在某些特定情況下的執行效率,但並不能明顯提升Android程式的效能。

5、AS3.0如何整合JNI功能

  1. AS3.0中下載NDK,CMake(高階的編譯配置工具),LLDB(高效的C/C++的偵錯程式)
  2. Java的native方法,通過javah生成對應的標頭檔案
  3. 編寫C/C++程式碼
  4. CMakeList檔案進行配置

6、cpp程式碼中JNIEXPORTJNICALL關鍵字的作用?

  1. 這兩個關鍵字是兩個巨集定義
  2. 用於說明該函式為JNI函式, Java虛擬機器載入的時候會連結對應的native方法

7、Java虛擬機器載入so庫時,如何找到Java層中對應的hative方法?(靜態註冊)

  1. 通過JNI函式的函式名
    去匹配:Java_PackageName_ClassName_NativeMethodName
  2. 可以在app/build/intermediates/classes/debug通過javah -d jni 包名.類名debug目錄下生成jni目錄(-d jni引數指定),並在其中生成對應的H標頭檔案

8、自動生成的JNI函式中的引數jobject是什麼?

  1. 當前和該JNI函式所連線的native方法所屬的類物件(等價於Java中的this)

9、JNI的動態註冊是什麼?

  1. 靜態註冊native方法的過程,是將Java層的native方法JNI函式一一對應
  2. 動態註冊能讓Java層的native方法和任意JNI函式連線起來。

10、JNI進行動態註冊的步驟

  1. Java中定義native函式
  2. C/C++中定義對應的JNI函式(名稱隨意不需要對應)
  3. C/C++中定義JNINativeMethod的資料—指明多組native函式和JNI函式對應關係
  4. C/C++中的JNI_OnLoad()中進行動態註冊
public class JniUtils2 {
    static {
        System.loadLibrary("native-lib");
    }
    public native String getStringFromC();

    public native void dynamicFunction();

    public native void dynamicFunction2();
}
#include "com_hao_jniapp_JniUtils2.h"
#include "android/log.h"

extern "C" {
JNIEXPORT jstring JNICALL Java_com_hao_jniapp_JniUtils2_getStringFromC
        (JNIEnv *env, jobject jobject1) {
    return env->NewStringUTF("我是來自JniUtils2的Native方法");
}

// 6. JNI函式(Java層的native方法的具體實現)
static void jniDynamicLog(JNIEnv *evn, jobject obj){
    __android_log_print(ANDROID_LOG_INFO, "feather", "JNI's Log: hello Java! Im from JNI");
}
static void jniTest(JNIEnv *evn, jobject obj){
    __android_log_print(ANDROID_LOG_INFO, "feather", "JNI's Log: hello Java! Im from JNI's Test");
}

/**==========================
 * 7. JNINativeMethod結構體:記錄java的native方法和JNI函式的對應關係
 *==========================*/
JNINativeMethod nativeMethod[] = {  {"dynamicFunction", //1. Java層native方法的方法名
                                            "()V",      //2. Java層native方法的描述符
                                            (void*)jniDynamicLog} //3. JNI函式的指標
                    ,{"dynamicFunction2", "()V", (void*)jniTest}  //4. 另一個繫結關係的native/jni
};

/**===========================================================
 * 1. JNI_OnLoad會在Java中的`System.loadLibarary()`載入so庫的時候呼叫
 * @param jvm *jvm為Java虛擬機器例項,JavaVM為一個結構體。
 *==========================================================*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {

    JNIEnv *env;
    //2. *jvm呼叫GetEnv()會獲得JNIEnv變數
    if (jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

        return -1;
    }
    __android_log_print(ANDROID_LOG_INFO, "feather", "JNI's Log: JNI_OnLoad...");
    //3. JNIEnv結構體指向一個函式表, 獲取到JniUtils物件
    jclass clz = env->FindClass("com/hao/jniapp/JniUtils2");
    //4. 將JniUtils物件和nativeMethod結構體所描述的Java層native方法和JNI函式 進行動態註冊
    env->RegisterNatives(clz, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]));

    //5. return 當前使用的JNI版本
    return JNI_VERSION_1_4;
}

}

11、JNINativeMethod的作用

  1. 作為一個結構體,描述了native方法和JNI函式的繫結關係
typedef struct {
    const char* name;//Java層native方法的名字
    const char* signature;//Java層native方法的描述符
    void*       fnPtr;//對應JNI函式的指標
} JNINativeMethod;

12、JNIEnv結構體的作用

  1. 指向一個函式表,該函式表指向一系列JNI函式
  2. 通過這些JNI函式就能夠實現呼叫Java層的程式碼
//1. 獲取到Java物件中某個變數的ID
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
//2. 根據變數的ID獲取到資料型別為boolean的變數
jboolean GetBooleanField(jobject obj, jfieldID fieldID)
//3. 獲取到Java物件中對應方法的ID
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
//4. 根據方法ID去呼叫對應物件中的方法,且該方法返回void
CallVoidMethod(jobject obj, jmethodID methodID, ...)
//5. 根據方法ID去呼叫對應物件中的方法,且該方法返回boolean
CallBooleanMethod(jobject obj, jmethodID methodID, ...)

13、JNI資料型別的作用

  1. Java層和C/C++的資料型別和物件不能直接相互引用和使用。
  2. C/C++的指標對於Java就是無法識別的。
  3. 綜上:JNI層定義了自己的資料型別以達到銜接的作用。

14、JNI中有哪幾種資料型別?

  1. JNI的資料型別分為兩種:基本型別和引用型別

15、JNI的原始資料型別

Java Type Native Typ Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void N/A

16、JNI引用型別?

  1. JNI定義了一些引用型別來便於JNI層呼叫
jobject                     (all Java objects)
|
|-- jclass                  (java.lang.Class objects)
|-- jstring                 (java.lang.String objects)
|-- jarray                  (array)
|     |--jobjectArray       (object arrays)
|     |--jbooleanArray      (boolean arrays)
|     |--jbyteArray         (byte arrays)
|     |--jcharArray         (char arrays)
|     |--jshortArray        (short arrays)
|     |--jintArray          (int arrays)
|     |--jlongArray         (long arrays)
|     |--jfloatArray        (float arrays)
|     |--jdoubleArray       (double arrays)
|
|--jthrowable

17、JNI中的方法ID和變數ID的作用?

  1. 如果要在在JNI中去呼叫Java層的某個方法,首先需要獲取它的ID,再根據ID通過JNI函式去獲得該方法。
//變數ID
struct _jfieldID;
typedef struct _jfieldID *jfieldID;

//方法ID
struct _jmethodID;
typedef struct _jmethodID *jmethodID; /* method IDs */

18、JNI中類描述符的作用

  1. 用於去獲取Java的物件。
  2. 例如com.hao.jniapp.JniUtils2這是該類所屬於的包。需要更換為com/hao/jniapp/JniUtils2-這就是類的描述符(FindClass("com/hao/jniapp/JniUtils2")中常用)。
    3.

19、方法描述符?

  1. 就是用於確定native方法的引數和返回值。
  2. “()V”中“V”表示返回值為空
  3. “()V”中“()”標識為引數
Method Descriptor Java Language Type
“()Ljava/lang/String;” String f();
“(ILjava/lang/Class;)J” long f(int i, Class c);
“([B)V” String(byte[] bytes);

20、資料型別描述符

Field Desciptor Java Language Type
Z boolean
B byte
C char
S short
I int
J long
F floa
D double

21、陣列描述符

  1. 一位陣列的描述符是“[+對應的型別描述符”
  2. 二維和三維陣列,以”[[“和“[[[”開頭,更多維陣列類似
Descriptor Java Langauage Type
“[[I” int[][]
“[[[D” double[][][]
Field Desciptor Java Language Type
“Ljava/lang/String;” String
“[Ljava/lang/Object;” Object[]

22、JNI函式中呼叫Java的靜態方法

  1. Java層的native方法所在類編寫一個一般方法
  2. C/C++的檔案中編寫函式,引數需要有(JNIEnv *env, jobject thiz),在內部去獲得Java層方法並呼叫。
  3. 在C/C++的JNI函式中去呼叫上者的方法。
//JniUtils2.Java
public class JniUtils2 {
    static {
        System.loadLibrary("native-lib");
    }
    ...
  //1. 靜態方法
    public static void staticShowMsg(String msg){
        Log.i("feather", "I'm Java Static method: get JNI's msg =" + msg);
    }
}
//xxx.cpp
void callJavaMethod(JNIEnv *env, jobject thiz){
  //1. 獲取到Java的class
    jclass clazz = env->FindClass("com/hao/jniapp/JniUtils2");
    if(clazz == NULL){
        printf("find class JniUtils2 error!");
        return;
    }
  //2. 獲取到方法ID
    jmethodID  id = env->GetStaticMethodID(clazz, "staticShowMsg", "(Ljava/lang/String;)V");
    if(id == NULL){
        printf("find method staticShowMsg error!");
    }
  //3. 呼叫該靜態方法並傳入引數
    jstring msg = env->NewStringUTF("Hello Java! I'm JNI message");
    env->CallStaticVoidMethod(clazz, id, msg);
}

23、JNI函式中呼叫Java的非靜態方法

重點是通過JNIEnv非靜態方法去呼叫,並且需要將對應的Java物件的this作為引數傳入。

//java
public class JniUtils2 {
    static {
        System.loadLibrary("native-lib");
    }
    ...
    public void showMsg(String msg){
        Log.i("feather", "I'm Java method: get msg =" + msg + "from JNI");
    }
}
void callAnothorJavaMethod(JNIEnv *env, jobject thiz){
    jclass clazz = env->FindClass("com/hao/jniapp/JniUtils2");
    if(clazz == NULL){
        printf("find class JniUtils error!");
        return;
    }
  //1. 獲取非靜態方法的ID
    jmethodID  id = env->GetMethodID(clazz, "showMsg", "(Ljava/lang/String;)V");
    if(id == NULL){
        printf("find method showMsg error!");
    }
    jstring msg = env->NewStringUTF("Hello Java! I'm JNI message");
  //2. 重點是需要將代表JniUtils2物件的this指標(`jobject thiz`)
    env->CallVoidMethod(thiz, id, msg);
}

參考書籍

相關推薦

JNINDK程式設計知識點

總結歸納JNI和NDK相關的知識點。 JNI和NDK程式設計 版本:2018/3/18-1(11:36 JNI和NDK簡介 1、JNI是什麼?作用? Java Native Interface(java本地介面) 方便

Android JNINDK學習(09)--JNI實例二 傳遞類對象

get state 回調 obj utf 說明 called [] code 1 應用層代碼 NdkParam.java是JNI函數的調用類,它的代碼如下: package com.skywang.ndk; import android.app.Activity;

JNINDK

roi 代碼 strong car net develop eve 場景 目的 摘:https://blog.csdn.net/carson_ho/article/details/73250163 JNI介紹定義:Java Native Interface,即 Java

淺學JNINDK

介紹 c++ google 什麽 為什麽 文章內容 特點 文件 view 作者:十歲的小男孩 QQ:929994365 心之安處即是吾鄉 前言   本文試圖通過解答以下三個問題來達到學習JNI和NDK的目的。是什麽?有什麽用?怎麽用?文章內容前三節來自下面第一個鏈接的博主共

JNINDK開發(1)_建立JNI程式

開始學習JNI開發技術,在網上看了很多文章,但講解的都是基礎或者過時的技術,沒有系統的關於JNI和NDK的學習教程,現在我寫《JNI和NDK開發》系列文章,主要是記錄自己從零開始學習遇到的一些問題和知識點,希望對大家也有些幫助。對於文章,本人也是邊學邊寫, 所以可能會更新的慢一點

JNINDK學習(1)--搭建開發環境

文章轉自我的Github Blog CommonQ's Blog NDK簡介 NDK的好處: 1. 程式碼的保護,由於apk的java層程式碼很容易被反編譯,而C/C++庫反匯難度較大。 2. 在NDK中呼叫第三方C/C++庫,因為大部分的開源庫都是用C/C++程式碼編

Android JNINDK學習(03)--動態方式實現JNI

前面總結了靜態實現JNI的方法,本文介紹如何動態實現JNI:JNI在載入時,會呼叫JNI_OnLoad,而解除安裝時會呼叫JNI_

【專案知識點彙總】二、JNI程式碼編譯方式camke ndk 方式 -- Android Studio 操作

一、介紹 Android Studio 編譯JNI程式碼有兩種方式:cmake 和 ndk 方式 使用感受: 1、cmake方式會受到所用Android sdk版本的影響,主要是ndk的版本影響,沒有深入去探究原理 2、ndk方式可以跨Android sdk 版本執行

Android之SDK、NDKJNIso檔案

1.     SDK     Android SDK(AndroidSoftware Development Kit),即Android軟體開發工具包,Android的SDK基於Java實現,這意味著基於Android

Android NDK程式設計: JNI技巧

前言: 這篇文章是android官方文件(https://developer.android.com/training/articles/perf-jni), 講的非常贊. 有很多最佳實踐, 比如儲存classid/methodid, 管理執行緒, cpp和java執行緒互動, 異常UTF編碼, 最

java工作筆記:web 程式設計中關於jnijna兩種工具操作效能對比測試

       第一次發部落格有點緊張哈。        最近剛剛公司轉崗從底層C語言的編寫到做Java的web restful架構。其中需要呼叫底層C++程式碼庫。所以對於選擇哪種方法從Java呼叫C的程式碼做了簡單地學習和對比測試。在這裡把他們貼出了。希望能有大神出來指點

在android studio下配置gradle用ndk-buildndk-gbd編譯除錯JNI

因為要在舊版android在做一些工作。所以做用到了它。目標平臺是:android api 10和armv6. 開發環境是:AS

關於echarts、layer.jsjqGrid的知識點

spa params h+ item getdate 正則 math grid 參數 使用echarts和layer.js直接去官方文檔,能解決大部分問題。 但是有些問題,解釋不夠清楚,在這裏記錄一下。 1、echarts的使用 第一點:關於echarts的lab

android studio使用CMakeNDK,實現應用自身被卸載時打開某一網址

licensed 項目 右鍵 ava 開發工具 not per 2.0 idt 實現應用自身被卸載時打開某一網址的c代碼 MyActivity: public class MyActivity extends Activity { /** * Calle

用隊列棧的知識點解決迷宮問題

margin 是否 port 迷宮 中一 post 左右 als info 迷宮問題 這裏有一個迷宮如圖所示,求走出迷宮的路徑 這裏我們建一個二維列表,表示迷宮(0表示通道,1表示圍墻)。 maze = [ [1,1,1,1,1,1,1,1,1,1],

Linux常用命令Shell程式設計基礎

目錄相關 cd - .與.. 分別表示當前目錄和父目錄 - ~與$HOME 都是指當前使用者的主目錄 - cd – 切換到上一次所在的目錄(不一定是父目錄) pwd - pwd 顯示當前目錄 - $PWD與$OLDPWD 兩個變量表示當前目錄和上一次所在的目錄 ls - ls -rtl 檢視

網路程式設計知識點剖析

網路程式設計知識點剖析 一. C/S 架構: Client / Server  客戶端 / 服務端     B/S 架構: Browser / Server  前端 / 服務端   二.網路程式設計通訊流程.   網絡卡   mac地址   IP地址   子網掩碼   

正則表示式re模組知識點彙總

"\^":匹配字元的開始"\$":匹配字元的結尾"[]":字元組"[^a]":如果在字元組中以^開頭,就是除了a不匹配,其他的都匹配"a|b":匹配字元a或b 注意:使用或關係的時候,要把長規則放在短規則的前面"()"分組,需要對一個整體匹配規則量詞約束的,就對整體匹配規則加一個括號字串最前面加上r 就是不

C指標--程式設計題9.14第10小題--判斷迴文函式

題目: 編寫函式   int palindrom( char *string); 如果引數字串是個迴文,函式就返回真,否則就返回假。迴文就是指一個字串從左向右讀和從右向左讀是一樣的。函式應忽略所有的非字母字元,而且在進行字元比較時不用區分大小寫。 前提是空白字元、標點符號和大小寫狀態被忽略,當Adam

快樂程式設計大本營【java語言訓練班】 6課:用java的物件程式設計

快樂程式設計大本營【java語言訓練班】 6課:用java的物件和類程式設計 第1節. 什麼是物件和類 第2節. 物件的屬性和方法 第3節. 類的繼承 第4節. 使用舉例:建立類,定義方法,定義屬性 第5節. 使用舉例:建立物件,屬性賦值與使用,方法呼叫; 第6節. 使用舉例:類繼承及物件使用 地址如下