用AS编写第一个so
用AS编写第一个so
我的gradle是
3.5.1
, Android Studio的版本是3.5.1
找了两个小时的博客, 硬是没找到能用的... 算了, 自力更生, 丰衣足食
0x00 环境配置
- 确定安装了ndk, cmake, LLDB
![](https://img.haomeiwen.com/i12577349/f8960591f215911b.png)
![](https://img.haomeiwen.com/i12577349/368e906fbe1c0122.png)
- 新建项目的时候, 记得选择
Native C++
之前版本的
include c++ support
选项在这个版本中已经独立出去了...
![](https://img.haomeiwen.com/i12577349/811492d458b6655a.png)
![](https://img.haomeiwen.com/i12577349/0978bddf00f83d58.png)
![](https://img.haomeiwen.com/i12577349/a72bdf7d32a70836.png)
![](https://img.haomeiwen.com/i12577349/1d96f42bccbe3f39.png)
- 先配置好sdk的路径:
File
->Project Structure...
->SDK Location
->Android SDK Location
![](https://img.haomeiwen.com/i12577349/b15ed4ec64733cbb.png)
![](https://img.haomeiwen.com/i12577349/9dd02a5896fcde89.png)
在Android视图下, 打开local.properties
, 看到ndk的路径已经配置完成
![](https://img.haomeiwen.com/i12577349/48635f518922f5c6.png)
0x02 声明native方法
新建一个类, 声明native
方法. 这个类是java与C/C++交互的中介, 方法由java声明, 由C/C++实现
这里为了方便接下来的操作, 切换成Project
视图
![](https://img.haomeiwen.com/i12577349/5b05327297f74da9.png)
![](https://img.haomeiwen.com/i12577349/3a165d47383dac2e.png)
![](https://img.haomeiwen.com/i12577349/ee0a3613b41bd375.png)
public class myJNI {
static {
System.loadLibrary("JniTest"); // 要加载的so的名字
}
public static native String sayHello();
}
虽然这里的
sayHello()
方法报错了, 但是不用管
0x03 编译与头文件的生成
- 使用
javac
编译上述文件, 生成class文件
![](https://img.haomeiwen.com/i12577349/e0b818331d85e39a.png)
![](https://img.haomeiwen.com/i12577349/22695cc104f7f47f.png)
- 确认自己的包名和类名, 然后在
java目录
使用javah 包名.类名
命令生成.h
头文件, 然后就能看到生成了一个.h文件
注意一定要在
java层目录
下输入命令, 不然不会报错: 找不到xxx类
![](https://img.haomeiwen.com/i12577349/86b1c54665a3e00b.png)
- 将生成的
com_example_test_myJNI.h
拖到cpp
目录下
![](https://img.haomeiwen.com/i12577349/2f9e839ccf9a096b.png)
- 然后删掉原来
cpp
目录下的native-lib.cpp
![](https://img.haomeiwen.com/i12577349/2f97e5733aecc7cb.png)
0x04 实现头文件的函数
- 在
cpp
目录下新建main.c
![](https://img.haomeiwen.com/i12577349/2dfdb5f70612094c.png)
![](https://img.haomeiwen.com/i12577349/eac06a74114b3938.png)
-
main.c
中的内容, 首先是将头文件包括进来, 然后实现头文件中的sayHello
方法
![](https://img.haomeiwen.com/i12577349/2612b51ba44ff96e.png)
#include "com_example_test_myJNI.h"
JNIEXPORT jstring JNICALL Java_com_example_test_myJNI_sayHello(JNIEnv *env, jclass jobj)
{
return (*env)->NewStringUTF(env,"hello 52pojie!");
}
注意: 这时候可以看到上面报错了, 先别管, 下面我们就来解决它
- 由于我们使用CMake来生成so的, 所以要修改
CMakeLists.txt
来指定so名称
和so的源文件的相对路径
![](https://img.haomeiwen.com/i12577349/b8dff83ad6f4d436.png)
![](https://img.haomeiwen.com/i12577349/e46032fe6b48f325.png)
然后sync
一下
![](https://img.haomeiwen.com/i12577349/1a8101a668dd6bb1.png)
发现main.c
已经不报错了
![](https://img.haomeiwen.com/i12577349/b61c2d731128e3b5.png)
0x05 生成so库
Build
->Rebuild Project
来生成so库
![](https://img.haomeiwen.com/i12577349/9f71259290f25a2c.png)
生成的so在app\build\intermediates\cmake\debug\obj\
或者app\build\intermediates\merged_native_libs\debug\out\lib
或者app\build\intermediates\stripped_native_libs\debug\out\lib\
ps: 这几个路径中的so库, 我暂时不知道区别在哪...哪位大佬知道的话, 麻烦留言说一声.
![](https://img.haomeiwen.com/i12577349/6903f21d5e600db8.png)
注意: 这里的so名字 =
lib
+ 我们在CmakeLists.txt中起的名字 +.so
0x06 配置so库
在app/src/main
下新建jniLIB
目录, 并将生成的SO文件拷贝到该文件夹下
这里的so库, 我测试的时候选择的是
app\build\intermediates\merged_native_libs\debug\out\lib
路径下的, 其他的请自测
![](https://img.haomeiwen.com/i12577349/6f4338fae3dc8919.png)
![](https://img.haomeiwen.com/i12577349/b5472aec0b7a3740.png)
0x07 调用
打开MainActivity插入一条log来调用so中的sayHello
方法,并连接手机调试
![](https://img.haomeiwen.com/i12577349/302449e36c0fc62b.png)
![](https://img.haomeiwen.com/i12577349/41caa338f5c3cbe0.png)