本文采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,可适当缩放并在引用处附上图片所在的文章链接。
Android NDK
Android NDK
JNI之C&C++调用Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
//使用jni调用java的代码, 必须了解java的反射机制;
//在jni中反射调用java代码的话, 一定要写异常处理
// jni中异常处理:
if (pEnv->ExceptionCheck()) //这里异常判断一定要写
{
pEnv->ExceptionDescribe();
pEnv->ExceptionClear();//如果出现异常,不清除,返回也会崩溃;
}
//这里就直接贴jni代码了, 详细在注释里说明;
//异常处理
#define isException() if (pEnv->ExceptionCheck())\
{pEnv->ExceptionDescribe();pEnv->ExceptionClear();}
JNIEXPORT jint JNICALL Java_org_bing_testjni_MainActivity_Add
(JNIEnv *env, jobject obj, jint a, jint b)
{
//通过包名或的类的类型
//如果是静态函数, 那么第二个参数就是jclass
jclass clsMainActivity = pEnv->FindClass("cr25/myjni/MainActivity");
isException();//异常处理
//获取构造
//参数1: 类的类型jclass
//参数2: 构造函数名称, java中所有类的构造函数名都是”<init>”
//参数3: 记录了参数个数, 类型, 返回值类型, 下面会详细说
jmethodID methodConstruct = pEnv->GetMethodID(clsMainActivity, "<init>", "()V");
isException();
//创建类对象
jobject objMain = pEnv->NewObject(clsMainActivity, methodConstruct);
isException();
//获取javaAdd方法地址
jmethodID JavaAdd= pEnv->GetMethodID(clsMainActivity,"JavaAdd", "(II)I");
isException();
//执行函数, 注意这个CallXXXMethod, XXX是要根据返回值类型来确定
//如: CallByteMethod, CallStringMethod, CallFloatMethod
//因为在JAVA反射中可以用Object统一返回值, C++没法来统一返回值
int resutl = pEnv->CallIntMethod(objMain , JavaAdd, a, b);
//到这里就调用完了函数, 还说一下字段获取与修改
//获取字段
//参数1: jclass
//参数2: 字段名
//参数3: 字段类型记录
jfieldID mData = pEnv->GetFieldID(clsMainActivity, "mData", "I");
//设置字段的值
pEnv->SetIntField(objMainActivity, mData, 2);
return resutl ;
}
|
Daalvik字节码类型描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
单独说一下GetMethodID与GetFieldID第三个参数意义,
其实这些字符串就是代表了参数与返回值类型的的简写;
括号内的代表参数, 括号外代表返回值
如: (II)V
I是代表int , V是代表viod, 表示有两个int的参数, 返回值是void类型
如: (Ljava/lang/String;ILjava/lang/String;)J
Ljava/lang/String;代表是类的类型, 格式为L[包名]; 注意是”;”结束
J是代表Long
表示有三个参数, String, int, String, 返回值为Long
V void 只能用于返回值类型
Z boolean 变量值只能是 真(true) 或 假(false)
B byte 字节型
S short 短整型
C char 字符型
I int 整数型
J long(64位) 长整型
F float 浮点型
D double(64位) 双精度浮点型
L java类类型 package.name.ObjectName Lpackage/name/ObjectName;
[ 数组类型 [I表示一个整型一维数组,等于java中的int[]。注意多位数数组的维数最大为255个。
[[I=int[][]
[[[I=int[][][]
|
NDK-动态和静态库
1. 静态库
1.1. 静态库编译流程
gcc——> obj 文件—->多个obj使用ar—–>静态库文件
需要用到的核心命令:
1
2
3
4
|
gcc –g –c mod1.c mod2.c mod3.c 编译obj (debug版)
AR r libdemo.a mod1.o mod2.o mod3.o 将3个obj 添加到libdemo.a中
AR tv libdemo.a 查看libdemo.a中的obj文件
AR d libdemo.a mod3.o 将mod3.o从libdemo.a中删除
|
1.2. 静态库使用
代码中包含头文件, 在链接的时候使用libdemo.a链接即可;
2. 动态库
2.1. 动态库编译流程
gcc——> obj 文件—->多个obj使用gcc的 -shared—–>so文件
需要用到的核心命令:
1
2
3
4
5
6
7
8
|
gcc -g -c -fpic -Wall mod1.c mod2.c mod3.c
gcc -g -shared mod1.o mod2.o mod3.o -o libfoo.so
gcc -g -fpic -Wall -shared mod1.o mod2.o mod3.o -o libfoo.so
-fpic 表示代码中地址全部使用相对地址, 不使用绝对地址,解决重定位
-c 只编译不链接
-Wall 显示所有警告
-g 生成Debug版
|
2.2. 动态库使用
- 静态使用
直接把So文件当作obj一起链接到文件, 文件中会存so信息, 在使用时会在目录中搜索so
linux动态库搜索顺序: system -> lib -> 环境变量
增加新环境变量, 用于系统搜索到我们的so, 这个环境变量是一次性的
adb shell export LD_LIBRARY_PATH += /data/local/tmp
- 动态使用
使用api加载指定目录的so, 再使用;
dlopen打开一个so文件
dlsym根据函数名拿到函数指针
一个工程代码如果同时有静态和动态编译, 那么就只编译动态库;
2.3. readelf工具
使用readelf工具可以查看elf文件的符号表
命令: 平台-readelf - s elf文件 ,当value值为0表示为导入函数
1
|
__attribute__() //关键字可以指定函数属性
|
如:
1
2
|
__attribute__(visibility("hidden")) // 隐藏函数
__attribute__(visibility("defult")) // 导出函数
|
以下在so的装载和卸载的时候被调用(老版本, 已经被淘汰, 但是可以用);
1
2
|
void _init() // 初始化函数
void _fini() // 反初始化函数
|
在新版本的初始化与反初始化机制中, 可以设置多个函数, 名字可以自定义;
1
2
3
4
|
void __attribute__((constructor)) OnLoad() // 初始化函数
void __attribute__((destructor)) UnLoad() // 反初始化
void __attribute__((constructor)) OnLoad1() // 初始化函数1
void __attribute__((destructor)) UnLoad1() // 反初始化1
|
3. 总结
1.相关函数
1
2
3
4
|
dlopen()函数打开一个共享库
dlsym()函数在库中搜
dlclose() 函数光比之前dlopen打开的库
dlerror() 函数返回一个错误消息的字符串
|
2.隐藏函数
1
|
void __attribute__ ((visibility("hidden"))) fun() {}
|
- so构造析构(会在so加载和卸载的时候调用)
1
2
|
void __attribute__ ((constructor)) Load()
void __attribute__ ((destructor)) UnLoad()
|
-
_init()和_fini()函数 会在so加载和卸载时调用
-
_init函数比构造函数来的早 hidden后在用dlsym函数无法找到