天天看點

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