Android Studio 编译原生库的默认编译工具是 CMake。
由于很多现有项目都使用 ndk-build 编译工具包,因此 Android Studio 也支持 ndk-build。
不过,如果您要创建新的原生库,则应使用 CMake。
CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt
CMake 使用以下规范来为库文件命名:
lib库名称.so
CMakeLists 命令
指定Cmake的最小版本
cmake_minimum_required(VERSION 3.4.1)
设置项目名称
project(xxxx)
add_library( xxx //生成的文件名
STATIC/SHARED // STATIC 生成静态库 SHARED生产 动态或共享库
xxx.cpp //指定包含哪些源文件
xxxx.cpp
)
find_library(VAR name path) 查找到指定的预编译库,并将它的路径存储在变量中,默认的搜索路径为Cmake包含的系统库,因此NDK的公共库,只需要指定库的name即可。
target_link_libraries{
xxx //目标库
// 目标库需要连接的库 这里是find_library指定的变量名
${xxxx}
}
_cplusplus 为C++关键字 表示作用域内的代码为C++ 代码
extern "C" 为了保证C++和C是互通的
c语言不能直接引用声明了 extern "C" 的 C++ 头文件,需要声明其为外部函数
JNI基础知识
普通调用定义接口 : 可以在多个类中被调用
定向调用定义接口: 仅能在特定的java类中被调用
普通调用定义接口 动态注册
基本思想: 在调用System.loadLibrary的时候,会在C/C++ 文件中回调一个名为JNI_OnLoad()的函数,
在该函数里通过JNI提供的RegisterNatives()方法来将C/C++ 方法和java方法对应起来
JNI 接口定义 -> JNI接口映射 -> 注册JNI接口映射 -> 设置在共享库加载时 JNI接口映射
1.编写Java的native方法和C/C++的实现方法
2.通过方法签名信息 将java方法和C/C++ 方法一一对应
3.实现JNI_Onliad()方法
3.1.通过JavaVM 获取JNIENV
3.2.使用类名和对应起来的方法作为参数,调用JNI提供的函数RegisterNatives() 注册方法
typedef struct {
const char* name;
const char* signature;
void* fnPtr; //函数指针,指向native函数。前面都要接 (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参数有两个 分别是 int 和 long[] 函数返回值为 String 即函数的定义为:String getString(int a ,long[] b)
签名就应该是 :"(I[J)Ljava/lang/String;"(不要漏掉英文分号)
如果有内部类 则用 FileStatus;
总结 :
当熟悉动态注册后, 动态注册无疑是注册函数的更好方式, 唯一要注意的是注册函数时, 需要额外小心,
别把类名,函数名和签名写错了, 不然loadLibraries时会导致应用Crash
JNI调用java方法的过程
findClass -> GetMethodId(获取java方法的句柄) -> Call*Method(调用java方法)
JNI操作java变量的一般过程
FindClass -> GetFieldId(获取java变量的句柄) -》GetField/SetFieldId 操作java变量
jobject代表了在java端调用本地c/c++代码的那个类的一个实例(对象)
jclass : 为了能够在c/c++中使用java类JNI.h头文件中专门定义了jclass类型来表示java中的Class类
libthread
创建线程函数
mutex 互斥锁 mutex_ -> lock()/ mutex_->unlock()
且cstdio中的函数都是定义在一个名称空间std里面的,如果要调用这个名字空间的函数,必须得加std::或者在文件中声明using namespace std
在C++中, 如果的构造函数只有一个参数时, 那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象
C++ explicit 只能用于修饰只有一个参数的类构造函数,表该构造函数时显示的,而非隐式的。 类构造函数默认即声明为 implicit(隐式)
左结合
相当于有两个连续的同一优先级的运算符时左边运算符加了圆括号。
右结合
相当于有两个连续的同一优先级的运算符时右边运算符加了圆括号。
C++ 中的冒号(:)用法
(1)表示机构内位域的定义(即该变量占几个bit空间)
typedef struct _XXX{
unsigned char a:4;
unsigned char c;
} ; XXX
(2)构造函数后面的冒号起分割作用,是类给成员变量赋值的方法,初始化列表,更适用于成员变量的常量const型。
struct _XXX{
_XXX() : y(0xc0) {}
};
3) public:和private:后面的冒号,表示后面定义的所有成员都是公有或私有的,直到下一个"public:”或"private:”出现为止。"private:"为默认处理。
(4)类名冒号后面的是用来定义类的继承。
class 派生类名 : 继承方式 基类名
{
派生类的成员
};
继承方式:public、private和protected,默认处理是public。
双冒号(::)的用法
1、
作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分
如:A,B表示两个类,在A,B中都有成员member。那么
A::member就表示类A中的成员member
B::member就表示类B中的成员member
2、
全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用::来区分如:
char zhou; //全局变量
void sleep()
{
char zhou; //局部变量
char(局部变量) = char(局部变量) *char(局部变量) ;
::char(全局变量) =::char(全局变量) *char(局部变量);
}
3、
::是C++里的“作用域分解运算符”。比如声明了一个类A,类A里声明了一个成员函数voidf(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成voidA::f(),表示这个f()函数是类A的成员函数
c++ 中的 static_cast和dynamic_cast运用于继承关系类间的强制转化
static_cast< new_type >(expression)
dynamic_cast< new_type >(expression)
备注:new_type为目标数据类型,expression为原始数据类型变量或者表达式。
static_cast相当于传统的C语言里的强制转换,该运算符把expression转换为new_type类型,
用来强迫隐式转换如non-const对象转为const对象,编译时检查,用于非多态的转换,
可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性
尽量少使用转型操作,尤其是dynamic_cast,耗时较高,会导致性能的下降,尽量使用其他方法替代
c/c++ 中的可变参数 va系列解释
是采用宏定义来实现
#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 定义了一个指针ap, 用于指示可选的参数
va_start(ap, param)
使 参数列表指针arg_ptr指向函数参数列表中的第一个可选参数
param 是位于第一个可选参数之前的固定参数
va_end(ap)
清空参数列表, 并置参数指针ap无效.
va_arg(ap, type)
返回参数列表中指针ap所指的参数, 返回类型为type. 并使指针ap指向参数列表中下一个参数.返回的是可选参数, 不包括固定参数.
例子:
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);
}
}