1、新建項目
androidStudio老版本慢睡,需要勾選包含Kotlin,C++
新版本AS
項目結構對比:
a铡溪、添加C++的模塊漂辐,需要在其build.gradle文件中增加配置
可以配置第三方so庫,其存放路徑默認src/main/jniLibs
可以生成特定cpu架構的so庫
另外棕硫,so庫生成髓涯,也可以使用ndk-build指令來處理,不過需要配置哈扮,Application.mk,Android.mk文件
b纬纪、cmake文件
cmake_minimum_required(VERSION 3.4.1)?用來設置在編譯本地庫時我們需要的最小的cmake版本,AndroidStudio自動生成灶泵,我們幾乎不需要自己管
add_library用來設置編譯生成的本地庫的名字為native-lib育八,SHARED表示編譯生成的是動態(tài)鏈接庫(這個概念前面已經提到過了),src/main/cpp/native-lib.cpp表示參與編譯的文件的路徑赦邻,這里面可以寫多個文件的路徑髓棋。
find_library是用來添加一些我們在編譯我們的本地庫的時候需要依賴的一些庫,由于cmake已經知道系統(tǒng)庫的路徑惶洲,所以我們這里只是指定使用log庫按声,然后給log庫起別名為log-lib便于我們后面引用,此處的log庫是我們后面調試時需要用來打log日志的庫恬吕,是NDK為我們提供的签则。
target_link_libraries是為了關聯(lián)我們自己的庫和一些第三方庫或者系統(tǒng)庫,這里把我們把自己的庫native-lib庫和log庫關聯(lián)起來铐料。
c渐裂、C++文件編寫
d、聲明和加載
kotlin中使用external聲明钠惩,java使用native聲明柒凉;加載庫,使用System.loadLibrary()方法
2篓跛、NDK代碼編寫
2.1膝捞、java代碼調用原生代碼
自動生成的模板工程既是;首先聲明愧沟,然后靜態(tài)加載蔬咬,最后就可以像本地方法調用了
注意kotlin代碼生成native方法鲤遥,不能使用Alt+Enter自動創(chuàng)建,java可以林艘;所以最好盖奈,先用java聲明后,在把java文件轉化為kotlin文件北启;也可以自己手寫
2.1.1 對應數據類型
2.1.2卜朗、生成native方法
JNIEXPORT和JNICALL都是JNI的關鍵字,表示此函數是要被JNI調用的
extern "C":兩種用途咕村,C++代碼使用c文件场钉,引用c文件頭;c代碼使用c++的方法或者變量
java/kotlin聲明方法在native對應方法
[extern "C"]?JNIEXPORT 返回參數類型 方法名(JNIEnv *env, jobject instance[,?方法類型 方法參數] [, 參數類型 參數] ...)
? ? 返回參數類型懈涛,參數類型為java類型對應的jni類型
? ? 方法名:包名(.替換成_)+ _ + java方法名
? ? 方法的參數個數比java參數個數多了2個逛万;
? ? JNIEnv*指針,是一個指向線程-局部數據的指針批钠;提供各種函數實現虛擬機功能宇植。需要注意的是,env變量是線程線程相關的埋心,不可從一個線程傳遞env變量到另外一個線程指郁。
? ? jobject是java調用實例,如果是java中靜態(tài)方法拷呆,則這個參數變?yōu)閖class instance
那么在C++子線程中如何獲取JNIEnv指針呢闲坎?
首先在JNI回調方法JNI_OnLoad中緩存JavaVM指針
然后使用JavaVM的AttachCurrentThread方法,使當前線程附著在虛擬機茬斧,這樣就獲得了JNIEnv實例腰懂,不過使用完畢后,需要使用DetachCurrentThread釋放项秉,如下圖
其實java/kotlin方法的動態(tài)注冊也是在JNI_OnLoad方法中執(zhí)行處理的
2.2 調用java/kotlin(也就是JNIEnv的使用)
另外方法簽名: (參數簽名)返回值簽名
2.2.1 訪問域
? ? a绣溜、如果不存在jclass對象,則使用GetObjectClass獲取
? ? b娄蔼、通過GetFieldID/GetStaticFieldID 獲取?jfieldID
? ? c怖喻、 通過Get<Type>Field/GetStatic<Type>Field 獲取域值
2.2.2 調用方法
? ? a、如果不存在jclass對象岁诉,則使用GetObjectClass獲取
? ? b罢防、通過GetMethodID/GetStatiMethodID 獲取?jMethodID
? ? c、 通過Call<Type>Method/CallStatic<Type>Method,來調用方法
2.3 JNIEnv的字符串操作
新建字符串NewStringUTF:有native 字符串轉換為java字符串
獲取字符串GetStringUTFChars:java字符粗轉化為native字符串
釋放native字符串ReleaseStringUTFChars唉侄;獲取后需要釋放,否則內存泄漏
2.4 JNIEnv的數組操作
創(chuàng)建數組New<Type>Array 創(chuàng)建某個類型的數組
訪問數組Get<Type>ArrayRegion: 把java數組內容copy到native數組內
提交數組Set<Type>ArrayRegion: 把native數組內容copy到java數組內
直接操作java數據內存Get<Type>ArrayElements: native數組和java數組指向相同內容野建;最后參數是指向副本属划,還是堆中固定地址恬叹;
需要釋放Release<Type>ArrayElements;釋放模式有0同眯,JNI_COMMIT绽昼、JNI_ABORT三種模式
0:內容復制回java數組內,并釋放原生數組
JNI_COMMIT: 內容復制回java數組內须蜗,并不釋放原生數組
JNI_ABORT:釋放原生數組硅确,并不把native數組改變應用到java數組里
2.5 JNIEnv的字符緩存區(qū)
NewDirectByteBuffer:native緩存變換為java緩存
GetDirectBufferAddress: java緩存變換為native緩存
GetDirectBufferCapacity:獲得java緩存區(qū)大小
2.6JNIEnv操作java對象在JNI中引用范圍
局部引用:大部分JNI函數返回局部引用,局部引用不能再后續(xù)調用中被緩存以及重用明肮;? 刪除方法:DeleteLocalRef
全局引用:如果沒有被原生代碼顯示釋放菱农,則后續(xù)可用;NewGlobalRef 生成一個全局引用柿估,刪除?DeleteGlobalRef
弱全局引用:如果沒有被原生代碼顯示釋放循未,則后續(xù)可用;與全局引用不同的是秫舌,不阻止?jié)撛诘睦厥眨?
? ? ?生成:?NewWeakGlobalRef的妖,檢驗弱全局引用仍然指向活動的類:IsSameObject,刪除:DeleteWeakGlobalRef