閱讀本文前,前先閱讀 JNI-NDK 在AndroidStudio3.2.1版本集成方法(ndk-build方式),瞭解jni在AndroidStudio裏的集成步驟php
Java 原生接口 (JNI):JNI 是 Java 和 C++ 組件用以互相通訊的接口。html
如今說的是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的文檔
之後會用到不少方法,均可以在這裏查詢
在上面的方法裏,看到了不少奇奇怪怪的數據類型 jboolean jstring...,他們與java、c是如何對應的
咱們在jin.h裏還發現了這段代碼
查看數據類型的官方文檔 文檔截圖:
參考類型 JNI包含許多與不一樣類型的Java對象相對應的引用類型。JNI引用類型按層次結構組織,如圖3-1所示。
我直接複製了上篇博客的內容
˙注意這裏新建的是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++");
}
複製代碼
//引入剛纔生成的頭文件
#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提供的一種makefile文件,用來指定諸如編譯生成so庫名、引用的頭文件目錄、須要編譯的.c/.cpp文件和.a靜態庫文件等。要掌握jni,就必須熟練掌握Android.mk的語法規範。
這個文件rebuild後 androidstudio會自動生成,看個人 上篇博客能夠找到他的生成的路徑,有小坑請注意
摘自安卓Application.mk文件的屬性說明和基本寫法
使用androidstudio開發,不須要這個文件了,能夠在gradle裏配置相關屬性,詳情見 此文
下面的截圖來自C/C++ 靜態連接庫(.a) 與 動態連接庫(.so),想了解更多,請點擊查看
咱們常常看到下面這個圖,會生成多個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的類型簽名表示了一個特定的Java類型,這個類型能夠是方法和類,也能夠是數據類型。
以上截圖均來自JNI開發最佳實踐,想了解更多,請點擊查看
固然你能夠經過上面的規律,本身去寫出這方法簽名,可是本身寫,可能會寫錯,那麼咱們能夠經過javap -s來獲得方法簽名
先看一下個人目錄結構
個人Jni這個文件內容爲若是你找不到這個classes目錄,先rebuild項目,並且不一樣版本的Androidstudio,這個目錄路徑不必定同樣(可是確定在build文件夾下),例如Android Studio環境下 java方法簽名的獲取,這篇文章裏的classes路徑就與個人版本不同
/Users/apple/AndroidStudioProjects/GoodDemo/JniBsetPractice/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes
javap -s jni.study.com.jnibsetpractice.Jni
結果爲至此,關於jni裏的一些概念,都瞭解了一遍,接下來,要敲幾個demo,看看具體如何使用這些知識點