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