Android Studio 編譯原生庫(kù)的默認(rèn)編譯工具是 CMake姨蟋。
由于很多現(xiàn)有項(xiàng)目都使用 ndk-build 編譯工具包,因此 Android Studio 也支持 ndk-build吉拳。
不過(guò),如果您要?jiǎng)?chuàng)建新的原生庫(kù),則應(yīng)使用 CMake胎围。
CMake 構(gòu)建腳本是一個(gè)純文本文件,您必須將其命名為 CMakeLists.txt
CMake 使用以下規(guī)范來(lái)為庫(kù)文件命名:
lib庫(kù)名稱.so
CMakeLists 命令
指定Cmake的最小版本
cmake_minimum_required(VERSION 3.4.1)
設(shè)置項(xiàng)目名稱
project(xxxx)
add_library( xxx //生成的文件名
STATIC/SHARED // STATIC 生成靜態(tài)庫(kù) SHARED生產(chǎn) 動(dòng)態(tài)或共享庫(kù)
xxx.cpp //指定包含哪些源文件
xxxx.cpp
)
find_library(VAR name path) 查找到指定的預(yù)編譯庫(kù)德召,并將它的路徑存儲(chǔ)在變量中白魂,默認(rèn)的搜索路徑為Cmake包含的系統(tǒng)庫(kù),因此NDK的公共庫(kù)上岗,只需要指定庫(kù)的name即可福荸。
target_link_libraries{
xxx //目標(biāo)庫(kù)
// 目標(biāo)庫(kù)需要連接的庫(kù) 這里是find_library指定的變量名
${xxxx}
}
_cplusplus 為C++關(guān)鍵字 表示作用域內(nèi)的代碼為C++ 代碼
extern "C" 為了保證C++和C是互通的
c語(yǔ)言不能直接引用聲明了 extern "C" 的 C++ 頭文件,需要聲明其為外部函數(shù)
JNI基礎(chǔ)知識(shí)
普通調(diào)用定義接口 : 可以在多個(gè)類中被調(diào)用
定向調(diào)用定義接口: 僅能在特定的java類中被調(diào)用
普通調(diào)用定義接口 動(dòng)態(tài)注冊(cè)
基本思想: 在調(diào)用System.loadLibrary的時(shí)候液茎,會(huì)在C/C++ 文件中回調(diào)一個(gè)名為JNI_OnLoad()的函數(shù)逞姿,
在該函數(shù)里通過(guò)JNI提供的RegisterNatives()方法來(lái)將C/C++ 方法和java方法對(duì)應(yīng)起來(lái)
JNI 接口定義 -> JNI接口映射 -> 注冊(cè)JNI接口映射 -> 設(shè)置在共享庫(kù)加載時(shí) JNI接口映射
1.編寫(xiě)Java的native方法和C/C++的實(shí)現(xiàn)方法
2.通過(guò)方法簽名信息 將java方法和C/C++ 方法一一對(duì)應(yīng)
3.實(shí)現(xiàn)JNI_Onliad()方法
3.1.通過(guò)JavaVM 獲取JNIENV
3.2.使用類名和對(duì)應(yīng)起來(lái)的方法作為參數(shù),調(diào)用JNI提供的函數(shù)RegisterNatives() 注冊(cè)方法
typedef struct {
const char* name;
const char* signature;
void* fnPtr; //函數(shù)指針捆等,指向native函數(shù)滞造。前面都要接 (void *)
} JNINativeMethod;
JNI簽名:
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
void v;
jbooleanArray [Z;
jintArray [i;
jlongArray [J;
jdoubleArray [d;
jfloatArray [f;
jbyteArray [b;
jcharArray [c;
jshortArray [s;
jobject L完整包名加類名 ;
} jvalue;
例子
傳入的java參數(shù)有兩個(gè) 分別是 int 和 long[] 函數(shù)返回值為 String 即函數(shù)的定義為:String getString(int a ,long[] b)
簽名就應(yīng)該是 :"(I[J)Ljava/lang/String;"(不要漏掉英文分號(hào))
如果有內(nèi)部類 則用 FileStatus;
總結(jié) :
當(dāng)熟悉動(dòng)態(tài)注冊(cè)后, 動(dòng)態(tài)注冊(cè)無(wú)疑是注冊(cè)函數(shù)的更好方式, 唯一要注意的是注冊(cè)函數(shù)時(shí), 需要額外小心,
別把類名,函數(shù)名和簽名寫(xiě)錯(cuò)了, 不然loadLibraries時(shí)會(huì)導(dǎo)致應(yīng)用Crash
JNI調(diào)用java方法的過(guò)程
findClass -> GetMethodId(獲取java方法的句柄) -> Call*Method(調(diào)用java方法)
JNI操作java變量的一般過(guò)程
FindClass -> GetFieldId(獲取java變量的句柄) -》GetField/SetFieldId 操作java變量
jobject代表了在java端調(diào)用本地c/c++代碼的那個(gè)類的一個(gè)實(shí)例(對(duì)象)
jclass : 為了能夠在c/c++中使用java類JNI.h頭文件中專門(mén)定義了jclass類型來(lái)表示java中的Class類
libthread
創(chuàng)建線程函數(shù)
mutex 互斥鎖 mutex_ -> lock()/ mutex_->unlock()
<cstdio> cstdio是將stdio.h的內(nèi)容用C++頭文件的形式表示出來(lái)。stdio.h是C標(biāo)準(zhǔn)函數(shù)庫(kù)中的頭文件
且cstdio中的函數(shù)都是定義在一個(gè)名稱空間std里面的栋烤,如果要調(diào)用這個(gè)名字空間的函數(shù)谒养,必須得加std::或者在文件中聲明using namespace std
在C++中, 如果的構(gòu)造函數(shù)只有一個(gè)參數(shù)時(shí), 那么在編譯的時(shí)候就會(huì)有一個(gè)缺省的轉(zhuǎn)換操作:將該構(gòu)造函數(shù)對(duì)應(yīng)數(shù)據(jù)類型的數(shù)據(jù)轉(zhuǎn)換為該類對(duì)象
C++ explicit 只能用于修飾只有一個(gè)參數(shù)的類構(gòu)造函數(shù),表該構(gòu)造函數(shù)時(shí)顯示的,而非隱式的买窟。 類構(gòu)造函數(shù)默認(rèn)即聲明為 implicit(隱式)
左結(jié)合
相當(dāng)于有兩個(gè)連續(xù)的同一優(yōu)先級(jí)的運(yùn)算符時(shí)左邊運(yùn)算符加了圓括號(hào)丰泊。
右結(jié)合
相當(dāng)于有兩個(gè)連續(xù)的同一優(yōu)先級(jí)的運(yùn)算符時(shí)右邊運(yùn)算符加了圓括號(hào)。
C++ 中的冒號(hào)(:)用法
(1)表示機(jī)構(gòu)內(nèi)位域的定義(即該變量占幾個(gè)bit空間)
typedef struct _XXX{
unsigned char a:4;
unsigned char c;
} ; XXX
(2)構(gòu)造函數(shù)后面的冒號(hào)起分割作用始绍,是類給成員變量賦值的方法瞳购,初始化列表,更適用于成員變量的常量const型亏推。
struct _XXX{
_XXX() : y(0xc0) {}
};
3) public:和private:后面的冒號(hào)学赛,表示后面定義的所有成員都是公有或私有的,直到下一個(gè)"public:”或"private:”出現(xiàn)為止吞杭。"private:"為默認(rèn)處理盏浇。
(4)類名冒號(hào)后面的是用來(lái)定義類的繼承。
class 派生類名 : 繼承方式 基類名
{
派生類的成員
};
繼承方式:public芽狗、private和protected绢掰,默認(rèn)處理是public。
雙冒號(hào)(::)的用法
1童擎、
作用域符號(hào)::的前面一般是類名稱滴劲,后面一般是該類的成員名稱,C++為例避免不同的類有名稱相同的成員而采用作用域的方式進(jìn)行區(qū)分
如:A,B表示兩個(gè)類顾复,在A,B中都有成員member哑芹。那么
A::member就表示類A中的成員member
B::member就表示類B中的成員member
2、
全局作用域符號(hào):當(dāng)全局變量在局部函數(shù)中與其中某個(gè)變量重名捕透,那么就可以用::來(lái)區(qū)分如:
char zhou; //全局變量
void sleep()
{
char zhou; //局部變量
char(局部變量) = char(局部變量) *char(局部變量) ;
::char(全局變量) =::char(全局變量) *char(局部變量);
}
3聪姿、
::是C++里的“作用域分解運(yùn)算符”。比如聲明了一個(gè)類A乙嘀,類A里聲明了一個(gè)成員函數(shù)voidf()末购,但沒(méi)有在類的聲明里給出f的定義,那么在類外定義f時(shí)虎谢,就要寫(xiě)成voidA::f()盟榴,表示這個(gè)f()函數(shù)是類A的成員函數(shù)
c++ 中的 static_cast和dynamic_cast運(yùn)用于繼承關(guān)系類間的強(qiáng)制轉(zhuǎn)化
static_cast< new_type >(expression)
dynamic_cast< new_type >(expression)
備注:new_type為目標(biāo)數(shù)據(jù)類型,expression為原始數(shù)據(jù)類型變量或者表達(dá)式婴噩。
static_cast相當(dāng)于傳統(tǒng)的C語(yǔ)言里的強(qiáng)制轉(zhuǎn)換擎场,該運(yùn)算符把expression轉(zhuǎn)換為new_type類型,
用來(lái)強(qiáng)迫隱式轉(zhuǎn)換如non-const對(duì)象轉(zhuǎn)為const對(duì)象几莽,編譯時(shí)檢查迅办,用于非多態(tài)的轉(zhuǎn)換,
可以轉(zhuǎn)換指針及其他章蚣,但沒(méi)有運(yùn)行時(shí)類型檢查來(lái)保證轉(zhuǎn)換的安全性站欺。它主要有如下幾種用法:
①用于類層次結(jié)構(gòu)中基類(父類)和派生類(子類)之間指針或引用的轉(zhuǎn)換。
進(jìn)行上行轉(zhuǎn)換(把派生類的指針或引用轉(zhuǎn)換成基類表示)是安全的;
進(jìn)行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成派生類表示)時(shí)矾策,由于沒(méi)有動(dòng)態(tài)類型檢查磷账,所以是不安全的。
②用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換贾虽,如把int轉(zhuǎn)換成char逃糟,把int轉(zhuǎn)換成enum。
③把空指針轉(zhuǎn)換成目標(biāo)類型的空指針蓬豁。
④把任何類型的表達(dá)式轉(zhuǎn)換成void類型履磨。
注意:static_cast不能轉(zhuǎn)換掉expression的const、volatile庆尘、或者_(dá)_unaligned屬性
盡量少使用轉(zhuǎn)型操作,尤其是dynamic_cast巷送,耗時(shí)較高驶忌,會(huì)導(dǎo)致性能的下降,盡量使用其他方法替代
c/c++ 中的可變參數(shù) va系列解釋
是采用宏定義來(lái)實(shí)現(xiàn)
#ifndef _VA_LIST
typedef __builtin_va_list va_list;
#define _VA_LIST
#endif
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
/* GCC always defines __va_copy, but does not define va_copy unless in c99 mode
* or -ansi is not specified, since it was not part of C90.
*/
#define __va_copy(d,s) __builtin_va_copy(d,s)
va_list 定義了一個(gè)指針ap, 用于指示可選的參數(shù)
va_start(ap, param)
使 參數(shù)列表指針arg_ptr指向函數(shù)參數(shù)列表中的第一個(gè)可選參數(shù)
param 是位于第一個(gè)可選參數(shù)之前的固定參數(shù)
va_end(ap)
清空參數(shù)列表, 并置參數(shù)指針ap無(wú)效.
va_arg(ap, type)
返回參數(shù)列表中指針ap所指的參數(shù), 返回類型為type. 并使指針ap指向參數(shù)列表中下一個(gè)參數(shù).返回的是可選參數(shù), 不包括固定參數(shù).
例子:
void AndroidLog::log(const char *fmt, ...) {
Lock fileLock(&mutex_);
if(!fmt){
return;
}
if (fp_ || openFile()){
va_list vp;
va_start(vp,fmt);
vfprintf(fp_,fmt,vp);
va_end(vp);
}
}