UnsatisfiedLinkError: dlopen "/data/data/<package>/app_jni_lib/libxxx.so is 32-bit instead of 64-bit

昨天掉到一个大坑里,本来说要增加arm64的支持,心想这还不简单,在libs目录下新建一个arm64-v8a的目录,然后对应的libxxx.so放进去就万事大吉了。提完patch,在三星Galaxy S6上试了一下没问题,就上线了~~  结果刚上GP半小时,出现好几十个崩溃,评分刷刷刷往下掉,吓死宝宝了。。。报的错都是说库是32位的:

java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/<package>/app_jni_lib/libxxx.so" is 32-bit instead of 64-bit
	at java.lang.Runtime.load(Runtime.java:331)
	at java.lang.System.load(System.java:981)
难道放错库了?用“file libxxx.so”检查了一下,确定我放的是64位版本的库:ELF 64-bit LSB  shared object, ARM aarch64, version 1 (SYSV), dynamically linked, stripped

另外,如果放错库的话,三星Galaxy S6上也应该会崩啊?又仔细看了下后台,发现这些出崩溃的手机没有一个是三星的,全部都是小米或者华为。于是找了台小米5过来试了一下,果然一启动就崩。

看了下加载这个so的代码,发现不是直接用System.load()来加载的,而是先判断/data/data/<package>/lib目录下有没有这个so,有的话就直接加载,没有的话就从apk的zip包里把lib/armeabi下面的库拷贝到/data/data/<package>/app_jni_lib目录下再加载。它这样做的目的很明显:为了支持armeabi / armeabi-v7a / arm64-v8a / x86,需要在这些目录下各提供一整套so,这样一来包的大小肯定会增加。因此先在armeabi里放一套so,加载的时候先判断有没有针对特定arch的so存在,有的话就加载,没有的话就把armeabi里的so拷过去用。

看一下查找库路径相关的代码:

 
 
private static File getLibFile(Context context, String libName) {
    return new File(context.getCacheDir().getParentFile().getAbsolutePath() + File.separator + "lib" + File.separator + getLibFileName(libName));
}
private static String getLibFileName(String libName) {
    return "lib" + libName + ".so";
}
最后拼成的路径就是/data/data/<package>/lib/libxxx.so,正常情况下是没有问题的,这个/data/data/<package>/lib其实是一个软链接,由installd创建,链接到apk解压的路径:

drwxrwx--x u0_a404  u0_a404           2016-11-08 12:46 databases
drwxrwx--x u0_a404  u0_a404           2016-11-08 12:46 files
lrwxrwxrwx install  install           2016-11-08 12:40 lib -> /data/app/<package>/lib/arm64
drwxrwx--x u0_a404  u0_a404           2016-11-08 13:08 shared_prefs
注意,我说的是正常情况,在小米跟华为手机上,压根就没有这个软链接!肯定是定制ROM的时候改东西了。。。这样一来,我们就找不到arm64对应的库了,然后根据上面的逻辑就会把lib/armeabi下面的32位的库拷贝到app_jni_lib目录下加载,然后就报上面的崩溃了~~

如此看来,上面那种查找库的方法是不可靠的。那么有没有什么靠谱点的方法呢?直接去PMS那边查呗:

private static File getLibFile(Context context, String libName) {
    ApplicationInfo applicationInfo = null;
    try {
        applicationInfo = getPackageManager().getApplicationInfo(getPackageName(), 0); 
    } catch (NameNotFoundException e) {
    }

    if (applicationInfo != null) {
        return new File(applicationInfo.nativeLibraryDir + File.separator + getLibFileName(libName));
    }
    return null;
}

private static String getLibFileName(String libName) {
    return "lib" + libName + ".so";
}
这个applicationInfo.nativeLibraryDir指向的其实就是原始路径/data/app/<package>/lib/arm64,这个东西是怎么都不会变的。

猜你喜欢

转载自blog.csdn.net/turkeycock/article/details/53089118