Android 加载动态连接库
Android 加载动态连接库(.so文件)有以下两种方法:
- 在 Java 中使用
System.loadLibrary
函数 - 在 NDK 中使用
dlopen/dlsym/dlclose
函数
System.loadLibrary 实现方式
为了能在 App 启动进入 Activity 前把动态连接库加载好,一般会选择把 loadLibrary 放在 static 中,写法如下:
public class MyClass {
static {
System.loadLibrary("mylib.so");
}
... ...
};
dlopen/dlsym/dlclose 实现方式
工作原理基本上是,dlopen 用来加载动态连接库,dlsym 从 dlopen 加载的动态链接库中按函数名获取函数指针。
例如,mylib.so 中有一个 add 函数:
int add(int a, int b) {
return a+b;
}
需要加载这个函数就可以这样来写:
static char libpath[MAX_PATH];
typedef int (*func_add)(int, int);
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
void* lib = dlopen(libpath, RTLD_NOW);
if (lib) {
func_add add = (func_add) dlsym(lib, "add");
if (add) add(1, 2);
dlclose(lib);
}
return JNI_VERSION_1_6;
}
这里遇到一个关键就是 dlopen 的第一个参数需要指明动态连接库的路径,这个路径怎么确定呢?如果知道这个路径的话直接写就可以了。当然,这里有个方法可以根据函数获得所在动态连接库路径。方法如下:
Dl_info info;
bzero(&info, sizeof(info));
if (dladdr((void*)&JNI_OnLoad, &info) != 0) {
char lineBuf[2048];
FILE* fp = fopen("/proc/self/smaps", "rt");
if (fp) {
char addrBuf[18];
sprintf(addrBuf, "%p-", info.dli_fbase);
int addrLength = strlen(addrBuf);
while (fgets(lineBuf, sizeof lineBuf, fp) != NULL) {
if(strncmp(lineBuf, addrBuf, addrLength) == 0
|| strncmp(lineBuf, addrBuf+2, addrLength-2) == 0) {
// .so文件记录
int lineLength = strlen(lineBuf);
int libNameLength = strlen(info.dli_fname);
for(int i = lineLength - 1; i >= 0 && isspace(lineBuf[i]); --i) {
lineBuf[i] = 0;
--lineLength;
}
if (strncmp(lineBuf + lineLength - libNameLength,
info.dli_fname, libNameLength) != 0) {
continue;
}
char* pathBegin = strchr(lineBuf, '/');
if (pathBegin == 0) continue;
char* pathEnd = strrchr(pathBegin, '/');
pathEnd[1] = 0;
// pathBegin此时指向的就是so文件所在目录的路径了
break;
}
}
fclose(fp);
}
}
用这个方式加载自由度会高很多,至少我这么认为的!哈哈!:)