Android APK调用JNI加载so动态库

动态库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文件中函数签名提示的一致。

注意事项
  1. 调用System.loadLibrary()函数加载库,load函数最好写在Application中,可确保一定会被加载,注意库加载时,如库名称为libtest.so,那么对应的load函数写法为System.loadLibrary("test");

  2. 底层库文件与上层Java JNI文件对应的包名或类名发生了变化,这需要核对JNI文件注册的地方JNIRegisterNativeMethods(),这个函数对应了上层Java JNI的包名和类名。

  3. 底层库文件与上层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

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值