
1.1 JNI(Java Native Interface)提供一種Java字節(jié)碼調(diào)用C/C的解決方案JNI描述的是一種技術(shù)1.2 NDK(Native Development Kit)Android NDK 是一組允許您將 C 或 C“原生代碼”嵌入到 Android 應(yīng)用中的工具NDK描述的是工具集。 能夠在 Android 應(yīng)用中使用原生代碼對于想執(zhí)行以下一項或多項操作的開發(fā)者特別有用在平臺之間移植其應(yīng)用。重復(fù)使用現(xiàn)有庫或者提供其自己的庫供重復(fù)使用。在某些情況下提高性能特別是像游戲這種計算密集型應(yīng)用。1.3 JNI注冊1.3.1 環(huán)境配置這里已有現(xiàn)成的文章就引用一下別人的了 講的很仔細(xì) 關(guān)于JNI環(huán)境配置1.3.2 靜態(tài)注冊當(dāng)Java層調(diào)用navtie函數(shù)時會在JNI庫中根據(jù)函數(shù)名查找對應(yīng)的JNI函數(shù)。如果沒找到會報錯。如果找到了則會在native函數(shù)與JNI函數(shù)之間建立關(guān)聯(lián)關(guān)系其實就是保存JNI函數(shù)的函數(shù)指針。下次再調(diào)用native函數(shù)就可以直接使用這個函數(shù)指針。JNI函數(shù)名格式包名里面的”.”需要改為”_”Java_ 包名com_example_auto_jnitest 類名(_MainActivity) 函數(shù)名(_stringFromJNI)靜態(tài)注冊缺點要求JNI函數(shù)的名字必須遵循JNI規(guī)范的命名格式名字冗長容易出錯初次調(diào)用會根據(jù)函數(shù)名去搜索JNI中對應(yīng)的函數(shù)會影響執(zhí)行效率需要編譯所有聲明了native函數(shù)的Java類每個所生成的class文件都要用javah工具生成一個頭文件靜態(tài)注冊例子類 JNITest 包名com.hqk.jnitestonepackage com.hqk.jnitestone; public class JNITest { static { System.loadLibrary(native-lib); } public static native String sayHello(); }對應(yīng)c代碼 cpp 類名為native-lib.cpp#include jni.h #include string extern C JNIEXPORT jstring JNICALL Java_com_hqk_jnitestone_JNITest_sayHello(JNIEnv *env, jclass clazz) { std::string hello Hello from C; return env-NewStringUTF(hello.c_str()); }注cpp 類名為native-lib.cpp則對應(yīng)java交互類 需要加入static { System.loadLibrary(native-lib); }java 對應(yīng)交互類的包名com.hqk.jnitestone則cpp對應(yīng)方法需要將對應(yīng)[.]轉(zhuǎn)化成[_](參照上面格式)1.3.2 動態(tài)注冊通過提供一個函數(shù)映射表注冊給JVM虛擬機(jī)這樣JVM就可以用函數(shù)映射表來調(diào)用相應(yīng)的函數(shù)就不必通過函數(shù)名來查找需要調(diào)用的函數(shù)。Java與JNI通過JNINativeMethod的結(jié)構(gòu)來建立函數(shù)映射表它在jni.h頭文件中定義其結(jié)構(gòu)內(nèi)容如下typedef struct { const char* name; // 對應(yīng)交互java 類對應(yīng)的方法名 const char* signature; //對應(yīng)交互方法的函數(shù)簽名 參考本文1.4.3 void* fnPtr; //對應(yīng)交互cpp方法的 指針函數(shù) 指向?qū)?yīng)函數(shù) } JNINativeMethod;1、創(chuàng)建映射表后調(diào)用RegisterNatives函數(shù)將映射表注冊給JVM; 2、當(dāng)Java層通過System.loadLibrary加載JNI庫時會在庫中查JNI_OnLoad函數(shù)。可將JNI_OnLoad視為JNI庫的入口函數(shù)需要在這里完成所有函數(shù)映射和動態(tài)注冊工作及其他一些初始化工作。概念說完了實際操作一下JAVA類 JNITest 包名com.hqk.jnitestonepackage com.hqk.jnitestone; public class JNITest { static { System.loadLibrary(native-lib); } public static native String sayHello(); public static native String sayHello2(); }注意這里多了一個 sayHello2 方法方法返回值為: StringCPP類名 native-lib.cpp 對應(yīng)代碼#include jni.h #include string #include android/log.h extern C JNIEXPORT jstring JNICALL Java_com_hqk_jnitestone_JNITest_sayHello(JNIEnv *env, jclass clazz) { std::string hello Hello from C; return env-NewStringUTF(hello.c_str()); } //cpp 交互方法 jstring sayHello2(JNIEnv *env, jobject thiz) { std::string hello Hello我是動態(tài)注冊成功的; return env-NewStringUTF(hello.c_str()); } // 動態(tài)注冊 函數(shù) 結(jié)構(gòu)數(shù)組 static const JNINativeMethod gMethods[] { {sayHello2, //對應(yīng)java交互類的方法名 ()Ljava/lang/String;, //對應(yīng)方法名的函數(shù)簽名 參考本文1.4.3 (jstring *) sayHello2 //對應(yīng) cpp交互類的指針函數(shù) } }; JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { __android_log_print(ANDROID_LOG_INFO, native, Jni_OnLoad); JNIEnv *env NULL; if (vm-GetEnv((void **) env, JNI_VERSION_1_4) ! JNI_OK) //從JavaVM獲取JNIEnv一般使用1.4的版本 return -1; //注意這里 FindClass 必須要和交互類的 包名對應(yīng)上并換成[/]符號 jclass clazz env-FindClass(com/hqk/jnitestone/JNITest); if (!clazz) { __android_log_print(ANDROID_LOG_INFO, native, cannot get class: com/hqk/jnitestone/JNITest); return -1; } if (env-RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) { __android_log_print(ANDROID_LOG_INFO, native, register native method failed!\n); return -1; } return JNI_VERSION_1_4; }從上面代碼可以看出1、CPP類里面 創(chuàng)建了 JNI_OnLoad 方法–》 該方法是在 初始化的時候執(zhí)行類似于 activity 的onCreate 核心代碼就是通過Find找到對應(yīng)Java類最后通過RegisterNatives 實現(xiàn)動態(tài)注冊 2、CPP類里面 新增了JNINativeMethod 類型的 結(jié)構(gòu)數(shù)組 3、CPP類里面 新增了 sayHello2 交互方法 4、Java類里面通用新增了 sayHello2方法1.4 數(shù)據(jù)類型轉(zhuǎn)換1.4.1 基本數(shù)據(jù)轉(zhuǎn)換1.4.2 引用數(shù)據(jù)類型轉(zhuǎn)換除了Class、String、Throwable和基本數(shù)據(jù)類型的數(shù)組外其余所有Java對象的數(shù)據(jù)類型在JNI中都用jobject表示。Java中的String也是引用類型但是由于使用頻率較高所以在JNI中單獨(dú)創(chuàng)建了一個jstring類型。引用類型不能直接在 Native 層使用需要根據(jù) JNI 函數(shù)進(jìn)行類型的轉(zhuǎn)化后才能使用;多維數(shù)組含二維數(shù)組都是引用類型需要使用 jobjectArray 類型存取其值例如二維整型數(shù)組就是指向一位數(shù)組的數(shù)組其聲明使用方式如下//獲得一維數(shù)組的類引用即jintArray類型 jclass intArrayClass env-FindClass([I); //構(gòu)造一個指向jintArray類一維數(shù)組的對象數(shù)組該對象數(shù)組初始大小為length類型為 jsize jobjectArray obejctIntArray env-NewObjectArray(length ,intArrayClass , NULL);1.4.3 JNI函數(shù)簽名信息由于Java支持函數(shù)重載因此僅僅根據(jù)函數(shù)名是沒法找到對應(yīng)的JNI函數(shù)。為了解決這個問題JNI將參數(shù)類型和返回值類型作為函數(shù)的簽名信息。JNI規(guī)范定義的函數(shù)簽名信息格式(參數(shù)1類型字符…)返回值類型字符函數(shù)簽名例子:3.JNI常用的數(shù)據(jù)類型及對應(yīng)字符:1.4.4 JNIEnv介紹JNIEnv概念 : JNIEnv是一個線程相關(guān)的結(jié)構(gòu)體, 該結(jié)構(gòu)體代表了 Java 在本線程的運(yùn)行環(huán)境。通過JNIEnv可以調(diào)用到一系列JNI系統(tǒng)函數(shù)。JNIEnv線程相關(guān)性 每個線程中都有一個 JNIEnv 指針。JNIEnv只在其所在線程有效, 它不能在線程之間進(jìn)行傳遞。注意在C創(chuàng)建的子線程中獲取JNIEnv要通過調(diào)用JavaVM的AttachCurrentThread函數(shù)獲得。在子線程退出時要調(diào)用JavaVM的DetachCurrentThread函數(shù)來釋放對應(yīng)的資源否則會出錯。JNIEnv 作用訪問Java成員變量和成員方法調(diào)用Java構(gòu)造方法創(chuàng)建Java對象等。1.5 JNI編譯1.5.1 Cmake編譯CMake 則是一個跨平臺的編譯工具它并不會直接編譯出對象而是根據(jù)自定義的語言規(guī)則CMakeLists.txt生成 對應(yīng) makefile 或 project 文件然后再調(diào)用底層的編譯 在Android Studio 2.2 之后支持Cmake編譯。cmake_minimum_required(VERSION 3.4.1) add_library( native-lib SHARED native-lib.cpp) find_library( log-lib log) target_link_libraries( native-lib ${log-lib})1.5.1.1 add_library 指令語法add_library(libname SHARED | STATIC | MODULE [source])將一組源文件 source 編譯出一個庫文件并保存為 libname.so (lib 前綴是生成文件時 CMake自動添加上去的)。其中有三種庫文件類型不寫的話默認(rèn)為 STATIC;SHARED: 表示動態(tài)庫可以在(Java)代碼中使用 System.loadLibrary(name) 動態(tài)調(diào)用STATIC: 表示靜態(tài)庫集成到代碼中會在編譯時調(diào)用MODULE: 只有在使用 dyId 的系統(tǒng)有效如果不支持 dyId則被當(dāng)作 SHARED 對待EXCLUDE_FROM_ALL: 表示這個庫不被默認(rèn)構(gòu)建除非其他組件依賴或手工構(gòu)建;#將compress.c 編譯成 libcompress.so 的共享庫 add_library(compress SHARED compress.c)1.5.1.2 target_link_libraries 指令語法target_link_libraries(target library debug | optimized library2…)這個指令可以用來為 target 添加需要的鏈接的共享庫同樣也可以用于為自己編寫的共享庫添加共享庫鏈接。如#指定 compress 工程需要用到 libjpeg 庫和 log 庫 target_link_libraries(compress libjpeg ${log-lib})1.5.1.3 find_library 指令語法find_library( name1 path1 path2 …)VAR 變量表示找到的庫全路徑包含庫文件名 。例如find_library(libX X11 /usr/lib) find_library(log-lib log) #路徑為空應(yīng)該是查找系統(tǒng)環(huán)境變量路徑1.5.1.4 更多詳細(xì)使用參考文獻(xiàn)更多關(guān)于Cmake的詳細(xì)使用1.5.2 Abi架構(gòu)ABIApplication binary interface應(yīng)用程序二進(jìn)制接口。不同的CPU 與指令集的每種組合都有定義的 ABI (應(yīng)用程序二進(jìn)制接口)一段程序只有遵循這個接口規(guī)范才能在該 CPU 上運(yùn)行所以同樣的程序代碼為了兼容多個不同的CPU需要為不同的 ABI 構(gòu)建不同的庫文件。當(dāng)然對于CPU來說不同的架構(gòu)并不意味著一定互不兼容。armeabi設(shè)備只兼容armeabiarmeabi-v7a設(shè)備兼容armeabi-v7a、armeabiarm64-v8a設(shè)備兼容arm64-v8a、armeabi-v7a、armeabiX86設(shè)備兼容X86、armeabiX86_64設(shè)備兼容X86_64、X86、armeabimips64設(shè)備兼容mips64、mipsmips只兼容mips根據(jù)以上的兼容總結(jié)我們還可以得到一些規(guī)律armeabi的SO文件基本上可以說是萬金油它能運(yùn)行在除了mips和mips64的設(shè)備上但在非armeabi設(shè)備上運(yùn)行性能還是有所損耗64位的CPU架構(gòu)總能向下兼容其對應(yīng)的32位指令集如x86_64兼容X86arm64-v8a兼容armeabi-v7amips64兼容mips以上就是NDK/JNI的入門內(nèi)容那么要如何進(jìn)階學(xué)習(xí)呢下面高能的地方來了有幸從一位字節(jié)跳動大神那里得到他本人吐血整理的“582頁Android NDK七大模塊學(xué)習(xí)寶典”從原理到實戰(zhàn)一應(yīng)俱全秉承好東西的當(dāng)然要共享的原則今天就來秀一把試試這“582頁Android NDK七大模塊學(xué)習(xí)寶典”是否也能讓你事半功倍這份寶典主要涉及以下幾個方面NDK 模塊開發(fā)JNI 模塊Native 開發(fā)工具Linux 編程底層圖片處理音視頻開發(fā)機(jī)器學(xué)習(xí)一、NDK 模塊開發(fā)主要內(nèi)容C與 C#數(shù)據(jù)類型總結(jié)C 與 C之內(nèi)存結(jié)構(gòu)與管理C 與 C之預(yù)處理命令與用 typedef 命名已有類型C 與 C之結(jié)構(gòu)體、共用體C 與 C之指針C/C 之多線程機(jī)制C/C 之函數(shù)與初始化列表二、JNI 模塊主要內(nèi)容JNI 開發(fā)之 靜態(tài)注冊與動態(tài)注冊靜態(tài)注冊、動態(tài)注冊、JNINativeMethod、數(shù)據(jù)類型映射、jni 函數(shù)默認(rèn)參數(shù)JNI 開發(fā)之方法簽名與 Java 通信Android NDK 開發(fā) JNI 類型簽名和方法簽名、JNI 實現(xiàn) java 與 c/c相互通訊JNI 開發(fā)之局部引用、全局引用和弱全局引用三、Native 開發(fā)工具主要內(nèi)容編譯器、打包工具與分析器十大最受歡迎的 React Native 應(yīng)用開發(fā)編輯器、react-native 打包流程靜態(tài)庫與動態(tài)庫CPU 架構(gòu)與注意事項ABI 管理、處理 CPU 功能、NEON 支持構(gòu)建腳本與構(gòu)建工具環(huán)境搭建、NDK 項目、Cmake、Makefile交叉編譯移植FFmpeg 編譯、FFmpegLIBX264FACC 交叉編譯 實現(xiàn) 264 流錄制、移植 FFmpeg 在 arm 交叉編譯時遇到的問題、FFmpeg 交叉編譯、X264 FAAC 交叉編譯、解決所有移植問題AS 構(gòu)建 NDK 項目配置 NDK 環(huán)境、建立 app 項目、生成.h 頭文件、創(chuàng)建 C 文件,實現(xiàn) native 方法、jni.h 文件四、Linux 編程Linux 環(huán)境搭建系統(tǒng)管理權(quán)限系統(tǒng)和工具使用vim 等Linux 環(huán)境的搭建、Linux 系統(tǒng)管理操作(25 個命令)Shell 腳本編程Shell 腳本、編寫簡單 Shell 腳本、流程控制語句、計劃任務(wù)服務(wù)程序五、底層圖片處理PNG/JPEG/WEBP 圖像處理與壓縮四種圖片格式、推薦幾種圖片處理網(wǎng)站、squoosh 在線無損圖片壓縮工具JPG/webP/PNG/ 互轉(zhuǎn)微信圖片壓縮計算原始寬高、計算近似寬高、第一次采樣獲取目標(biāo)圖片、循環(huán)逼近目標(biāo)大小GIF 合成原理與實現(xiàn)GIF 圖片的解析、GIF 圖片的合成序列圖像合成 GIF 圖像六、音視頻開發(fā)多媒體系統(tǒng)Camera 與手機(jī)屏幕采集、圖像原始數(shù)據(jù)格式 YUV420(NV21 與 YV12 等)、音頻采集與播放系統(tǒng)、編解碼器 MediaCodec、MediaMuxer 復(fù)用與 MediaExtractorFFmpegffmpeg 模塊介紹、音視頻解碼,音視頻同步、I 幀,B 幀,P 幀解碼原理、x264 視頻編碼與 faac 音頻編碼、OpenGL 繪制與 NativeWindow 繪制流媒體協(xié)議RTMP 協(xié)議、、音視頻通話 P2P WebRtcOpenGL ES 濾鏡開發(fā)之美顏效果高斯模糊、高反差保留、強(qiáng)光處理、融合抖音視頻效果分析與實現(xiàn)流程列表、視頻拍攝、視頻編輯、視頻導(dǎo)出音視頻變速原理變速入口分析、音頻變速實現(xiàn)、視頻變速實現(xiàn)七、機(jī)器學(xué)習(xí)Opencv圖像預(yù)處理灰度化和二值化、腐蝕與膨脹、人臉檢測、身份證識別最后由于篇幅限制文檔的詳解資料太全面細(xì)節(jié)內(nèi)容太多所以只把部分知識點截圖出來粗略的介紹每個小節(jié)點里面都有更細(xì)化的內(nèi)容除了上面的之外我還自己整理了以下一系列的學(xué)習(xí)進(jìn)階資料《Android開發(fā)七大模塊核心知識筆記》《2246頁最新Android大廠高頻面試題解析大全》由于篇幅限制這里只能展示部分內(nèi)容朋友們?nèi)绻枰@份完整版的PDF資料合集微信掃描下方CSDN官方二維碼【免費(fèi)獲取】。