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