JNI相關概念的理解

閱讀本文前,前先閱讀 JNI-NDK 在AndroidStudio3.2.1版本集成方法(ndk-build方式),瞭解jni在AndroidStudio裏的集成步驟php

概念

Java 原生接口 (JNI):JNI 是 Java 和 C++ 組件用以互相通訊的接口。html

理解JNI

先說說JNIEnv

如今說的是C裏的JNIEnv,不是C++裏的JNIEnv,有點區別,可是理解了C裏的JNIEnv,就理解了C++裏的JNIEnvjava

# include "jni_study_com_jnibsetpractice_Jni.h"

JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_sayHello
        (JNIEnv *env, jobject instance) {
    return (*env)->NewStringUTF(env, "Hello from C");
}
複製代碼

上面的代碼裏第一個參數是JNIEnv,這是個什麼東西,顧名思義jni環境,點進去跳到了 android

這個文件裏

發現 JNIEnv是JNINativeInterface*的別名,JNINativeInterface是什麼,發現他是一個結構體,裏面列出了許多方法指針,至關於java裏的一個類,類裏定義了不少方法,這些方法在jni開發中很是重要c++

看看這個方法是否是很眼熟,NewStringUTFbash

(*env)->NewStringUTF(env, "Hello from C");調用了這個方法,製造了一個字符串返給了java,你會問不就是一個字符串嗎,我直接"Hello from C"返回不就好了嗎?不行的,你知道C裏是沒有過String類型的,C裏的"xxx",java並不認識,這就須要jni (java native interface),即【java 與本地語言(C/C++)接口】來解決這個問題

看看這個方法,傳入一個*env和char *,char*就是C裏的字符串類型,返回了jstring(jstring是java裏的String的等價物),這樣就把C裏的字符串轉換成java能夠是別的字符串,你說這個方法重要嗎,他是結構體JNINativeInterface提供的。架構

除了這個方法,其餘不少方法都很重要,他的做用基本上就是一些jni開發中,經常使用到的一些方法,爲咱們在C和Java之間搭建了一座橋樑,讓彼此互相溝通oracle

下圖是JNINativeInterface結構體的一個圖示 app

JNINativeInterface裏全部方法的說明看這裏: 官方文檔ide

全部方法分爲如下幾類

看看NewStringUTF的文檔

之後會用到不少方法,均可以在這裏查詢

JNI裏的數據類型

在上面的方法裏,看到了不少奇奇怪怪的數據類型 jboolean jstring...,他們與java、c是如何對應的

咱們在jin.h裏還發現了這段代碼

查看數據類型的官方文檔 文檔截圖:

參考類型 JNI包含許多與不一樣類型的Java對象相對應的引用類型。JNI引用類型按層次結構組織,如圖3-1所示。

JNIENV在C與C++的區別

我直接複製了上篇博客的內容

  1. 建立實現頭文件的.cpp源文件 接下來要寫個c++代碼,實現這個jni接口

˙注意這裏新建的是c++代碼,c++代碼對應下面的代碼

//引入剛纔生成的頭文件
#include "ndkold_study_com_ndkolddemo_Java2CJNI.h"

//複製頭文件裏的要實現的方法名及其參數
JNIEXPORT jstring JNICALL
Java_ndkold_study_com_ndkolddemo_Java2CJNI_java2C(JNIEnv *env, jobject instance) {
//    實現這個方法,返回一個字符串
    return env->NewStringUTF("Hello from C++");
}
複製代碼
  1. 你也能夠寫個.c源文件,其對應代碼爲
//引入剛纔生成的頭文件
#include "ndkold_study_com_ndkolddemo_Java2CJNI.h"

//複製頭文件裏的要實現的方法名及其參數
JNIEXPORT jstring JNICALL
Java_ndkold_study_com_ndkolddemo_Java2CJNI_java2C(JNIEnv *env, jobject instance) {
//    實現這個方法,返回一個字符串
    return (*env)->NewStringUTF(env, "Hello from C");
    //注意這裏是(*env),並且須要傳遞一個參數(env)
}
複製代碼

說明:c與c++就這點區別,查看jni.h文件,發如今c裏的JNIEnv是結構體指針JNINativeInterface*的別名,因此JNIEnv *env至關於二級指針,如今要調用JNINativeInterface*裏的方法,要用(*env)->xxx

在c++裏JNIEnv是_JNIEnv的別名,在_JNIEnv內部裏有個屬性爲結構體指針JNINativeInterface*,而後他把全部c裏的方法都從新定義了一下,定義方式就是經過JNINativeInterface*調了一遍全部c裏的方法,並且把JNINativeInterface*的對象以this方式傳遞進去了,可見這裏的JNIEnv *env是一個一級指針,因此經過env就能夠直接調用對應的方法了(有點繞)

Android.mk文件

Android.mk是Android提供的一種makefile文件,用來指定諸如編譯生成so庫名、引用的頭文件目錄、須要編譯的.c/.cpp文件和.a靜態庫文件等。要掌握jni,就必須熟練掌握Android.mk的語法規範。

參考自Android.mk用法詳解

這個文件rebuild後 androidstudio會自動生成,看個人 上篇博客能夠找到他的生成的路徑,有小坑請注意

Application.mk文件

摘自安卓Application.mk文件的屬性說明和基本寫法

使用androidstudio開發,不須要這個文件了,能夠在gradle裏配置相關屬性,詳情見 此文

靜態連接庫(.a) 與 動態連接庫(.so)

咱們編譯成功的文件是一個.so文件,咱們的C代碼就打包在了這個文件中,相似於java的jar包。C編譯後的代碼有兩種格式文件靜態連接庫(.a) 與 動態連接庫(.so) 簡單說區別爲:

  1. 靜態連接庫文件大,裏面有你本身寫的邏輯,也有引用的其餘庫函數,可是容易移植
  2. 動態連接庫文件小,公用的代碼,能夠直接引用,肯定是不容易移植,由於so文件裏的代碼不是獨立能夠運行的

下面的截圖來自C/C++ 靜態連接庫(.a) 與 動態連接庫(.so),想了解更多,請點擊查看

靜態連接庫(.a)

動態連接庫(.so)

ABI(應用二進制接口)

咱們常常看到下面這個圖,會生成多個libxxx.so文件,他們分別在不一樣的armxxx下面

簡單來講,每一個手機裏的cpu是不一樣的,不一樣的cpu對應支持不一樣的ABI,咱們編譯出來的so文件,在不一樣ABI上是不能兼容的,因此咱們要針對不一樣的ABI變移除不一樣的so文件,這樣不一樣cpu的手機均可以調用到適合本身的so文件。

不一樣的 Android 手機使用不一樣的 CPU,而不一樣的 CPU 支持不一樣的指令集。CPU 與指令集的每種組合都有專屬的應用二進制接口,即 ABI。ABI 能夠很是精確地定義應用的機器代碼在運行時如何與系統交互。您必須爲應用要使用的每一個 CPU 架構指定 ABI。---摘自android官網-ABI 管理

常見的有armeabi、armeabi-v7a、arm64-v8a、x8六、x86_64,在abiFilters配置

defaultConfig {
        ...
        ndk {
            moduleName "Java2C"
            //so文件名,若是這裏配置了so文件名字,
            //記得更改Android.mk裏的
            //LOCAL_MODULE :字段爲 LOCAL_MODULE := Java2C
            abiFilters "armeabi", "armeabi-v7a", "x86" 
            //指定so文件所支持的CPU類型
            //若是不寫的話,會生成全部的CPU類型的so文件
        }
    }
複製代碼

再補充幾個概念

jni的類型簽名介紹

jni的類型簽名表示了一個特定的Java類型,這個類型能夠是方法和類,也能夠是數據類型。

以上截圖均來自JNI開發最佳實踐,想了解更多,請點擊查看

javap -s 獲取方法簽名

固然你能夠經過上面的規律,本身去寫出這方法簽名,可是本身寫,可能會寫錯,那麼咱們能夠經過javap -s來獲得方法簽名

先看一下個人目錄結構

個人Jni這個文件內容爲

若是你找不到這個classes目錄,先rebuild項目,並且不一樣版本的Androidstudio,這個目錄路徑不必定同樣(可是確定在build文件夾下),例如Android Studio環境下 java方法簽名的獲取,這篇文章裏的classes路徑就與個人版本不同

  1. 在命令行進入到classes cd /Users/apple/AndroidStudioProjects/GoodDemo/JniBsetPractice/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes
  2. 運行javap -s jni.study.com.jnibsetpractice.Jni 結果爲

紅色框框裏的就是方法簽名,你們最好這樣獲取,省的報錯了不知道(Jni調試很麻煩,咱們要儘可能少寫錯,因此最好複製)

小結

至此,關於jni裏的一些概念,都瞭解了一遍,接下來,要敲幾個demo,看看具體如何使用這些知識點

相關文章
相關標籤/搜索