动态库so的编写
android/linux的动态库是用C/C++编写的,其动态库的后缀名为so
so库的特点:lib开头+so库名+.so后缀,例如:libxxx.so
//其中com_example_jnigpio为package名
//GPIOControl为调用该.so的class
//方法名为exportGpio,参数为int gpio
/*
* Class: com_example_jnigpio_GPIOControl
* Method: exportGpio
* Signature: (I)I
*/
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnigpio_GPIOControl_exportGpio(JNIEnv *env, jobject instance, jint gpio)
{
char buffer[BUFFER_MAX];
int len;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0) {
LOGE("Failed to open export for writing!\n");
return(0);
}
len = snprintf(buffer, BUFFER_MAX, "%d", gpio);
if (write(fd, buffer, len) < 0) {
LOGE("Fail to export gpio!\n");
return 0;
}
close(fd);
return 1;
}
GPIOControl.c
对应的
Android.mk
,编译的结果是
libGPIOControl.so
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := GPIOControl
LOCAL_SRC_FILES := GPIOControl.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
APK使用动态库
1.将libxxx.so复制到app/libs目录下,在build.gradle引入lib
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
2.调用so库的native
static {
//加载so库的时候,需要掐头去尾,去掉lib和.so
System.loadLibrary("xxx");
}
示例:
public class GPIOControl{
static{
Log.e("fangjian",System.getProperty("java.library.path"));//打印查找lib库的路径
System.loadLibrary("GPIOControl");
}
public static native int exportGpio(intgpio);
}
//调用方式
GPIOControl gpioControl;
gpioControl.exportGpio(25);//导出GPIO25的节点
调用JNI报错
<1>
java.lang.UnsatisfiedLinkError: dlopen failed: library "libxxx.so",xxx is not accessible for the namespace
原因:
Google从Android 7开始,除了那些在Android NDK提供的库之外,限制了APK应用对系统私有库的加载,通过以前的方法去加载库会出现以上报错,因此我们需要将缺少的so文件放入apk中的lib目录中。默认的lib加载路径有:
/system/lib
、
/vendor/lib
、
/data/app/.../lib
解决:
1.在Android.mk中添加缺少的so文件,这样这些库就会编译进APK
LOCAL_JNI_SHARED_LIBRARIES += libnativeloader \
libc++
2.手动将so文件push到data/app/相关apk目录下的lib文件夹中。
<2>
java.lang.UnsatisfiedLinkError: No implementation found for
原因:
libxxx.so中函数声明涉及到的package name和class name与调用它的package name和class name不符。
解决:
改变工程中的package name和class name,使其与libxxx.so文件中函数签名提示的一致。
注意事项
- 调用System.loadLibrary()函数加载库,load函数最好写在Application中,可确保一定会被加载,注意库加载时,如库名称为libtest.so,那么对应的load函数写法为
System.loadLibrary("test");
- 底层库文件与上层Java JNI文件对应的包名或类名发生了变化,这需要核对JNI文件注册的地方JNIRegisterNativeMethods(),这个函数对应了上层Java JNI的包名和类名。
- 底层库文件与上层Java JNI文件对应的函数名称、函数参数等发生变化,需要核对JNINativeMethod gMethods[],JNI函数注册的地方。
相关参考
https://blog.csdn.net/a22796853/article/details/79709344
https://www.cnblogs.com/fordreamxin/p/4354017.html
https://blog.csdn.net/wl724120268/article/details/80475970
https://blog.csdn.net/pbm863521/article/details/79742708