Android 开发中遇到加载有相同函数的 so 库时的问题
1. 常见解决方案
Linux 在多个库存在同名风险的时候,例如多个库引用不同版本的开源代码,实现还不同,这时候会造成调用的冲突;
通过在头文件的函数接口前加上 “ attribute((visibility("default"))) ”开放该 so 包可见的函数接口;
在 Makefile 文件中通过“CXX=g++ -fvisibility=hidden”使得未开放的函数名都保护起来。
2. 解决过程
在项目中遇到了加载 so 库比较诡异的一个现象,现记录下来,以做总结。以下以举例的方式讲述:项目中有两个 so 库,一个是 libdemojni.so,一个是 libdemojni2.so,这两个库的都有相同函数,一个是动态注册的,一个是静态注册的,在程序启动时报错:“Failed to register native method com.example.fun.HelloJni.getSecond()Ljava/lang/String;”
和 “java.lang.UnsatisfiedLinkError: JNI_ERR returned from JNI_OnLoad in "/data/app/com.zxl.test-1/lib/arm64/libhellojni.so"
。但是,我们看 maps 表(cat /proc/进程名/maps |grep hellojni)如下:
发现虽然 libhellojni.so 加载出错了,但是还是被加载到内存中了。 执行前面的方法 testLib(),日志如下:
发现第一个方法 getString()调用到的是 libhellojni.so 库,而第二个方法 getFirst()调用到的竟然是 libhellojni2.so 中的方法。
出现此种情况的原因,应该是动态注册 和 静态注册 的缘故。当加载 libdemojni.so 时,动态注册了 getString()方法,而由于动态注册 getSecond()方法时,HelloJni.java 中没有声明此方法导致动态注册中断,而我们又在 java 代码中 catch 了这个异常,因此会继续加载 libhellojni2.so。在调用方法时,我们调用 getString()方法,由于这个方法动态注册成功了,因此会调用到 libhellojni.so 中,而再调用 getFirst()方法,这个方法动态注册失败了,因此系统不会去 libhellojni.so 中查找了,转而内存中加载的其他的 so 库中查找看有没有能够匹配 getFirst()方法的函数,结果在 libhellojni2.so 中查找到了,因此调用到 libhellojni2.so 库中。
3. 总结
出现这个现象的原因在于不了解机制和疏忽大意:
只在 cpp 中增加了对应的函数,并且增加的函数注册方法也不是放在 nativeMethods[]声明的最后,而是放到了中间。
忘记在 HelloJni.java 中增加对应的方法。
这种问题都是因为粗心引起,要小心严谨。
评论