在使用cmake解决Android中对第三方库的依赖一文中,我们通过cmake的配置来将依赖的so给链接了起来。但是有时我们可能需要在程序运行地时候来动态地加载依赖库。linux 提供了动态加载依赖库的系统调用,我们在 Android 中也可以使用。
- 将我们要依赖的 so 和工程中其他的 so 都放入到 jniLibs 目录下,这样所有的 so 文件都会打包到 App 中。当然被依赖的 so 也可以放入其他指定的目录。
获取到被依赖 so 的路径,如果被依赖 so 放在自定义的路径里,那这一步可以忽略。我们可以根据 App 里其他 so 的路径来得到被依赖 so 的路径。如被依赖的 so 为 liblayer1.so 而我们准备在 liblayer2.so 里面来动态地加载 liblayer1.so 就可以通过下面的程序来获取到 liblayer1.so 所在的目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36std::string get_self_dir() {
static const char *SELF_NAME = "/liblayer2.so";
static const size_t SELF_NAME_LEN = strlen(SELF_NAME);
FILE *fmap = fopen("/proc/self/maps", "r");
if (!fmap) {
LOG_E("failed to open maps");
return {};
}
std::unique_ptr<FILE, int (*)(FILE *)> fmap_close{fmap, ::fclose};
char linebuf[512];
while (fgets(linebuf, sizeof(linebuf), fmap)) {
uintptr_t begin, end;
char perm[10], offset[20], dev[10], inode[20], path_mem[256], *path;
int nr = sscanf(linebuf, "%zx-%zx %s %s %s %s %s", &begin, &end, perm,
offset, dev, inode, path_mem);
if (nr == 6) {
path = nullptr;
} else {
if (nr != 7) {
LOG_E("failed to parse map line: %s", linebuf);
return {};
}
path = path_mem;
}
if (path) {
auto len = strlen(path);
auto last_dir_end = path + len - SELF_NAME_LEN;
if (!strcmp(last_dir_end, SELF_NAME)) {
last_dir_end[1] = 0;
return path;
}
}
}
LOG_E("can not find path of %s", SELF_NAME + 1);
return {};
}将 dlfcn.h 头文件给引入进来
1
#include <dlfcn.h>
通过 dlopen 来加载 liblayer1.so 然后同过 dlsym 来获取到 liblayer1.so 对外暴露的符号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17NET_API_FUNCTIONS_TYPE_LAYER1 *client_layer1;
static std::string self_dir = get_self_dir();
if (self_dir.empty())
return;
std::string path{self_dir};
path.append("liblayer1.so");
LOG_E("dlopen %s:", path.c_str());
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
return;
}
auto sym = dlsym(handle, "net_client_layer1");
if (!sym) {
return;
}
client_layer1 = reinterpret_cast<NET_API_FUNCTIONS_TYPE_LAYER1 *>(sym);通过 client_layer1 就可以像原来一样调用 liblayer1.so 里的方法了。
完整代码请见Github