天天看点

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