動态庫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