为什么有些 Android 手机会导致我们的应用抛出 java.lang.UnsatisfiedLinkError? [英] Why do some Android phones cause our app to throw an java.lang.UnsatisfiedLinkError?

查看:23
本文介绍了为什么有些 Android 手机会导致我们的应用抛出 java.lang.UnsatisfiedLinkError?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们在市场上使用我们的应用程序的一些 Android 手机上遇到 java.lang.UnsatisfiedLinkError.

We're experiencing a java.lang.UnsatisfiedLinkError on some of the Android phones that are using our app in the market.

问题描述:

static
{
    System.loadLibrary("stlport_shared"); // C++ STL        
    System.loadLibrary("lib2"); 
    System.loadLibrary("lib3"); 
}

使用 java.lang.UnsatisfiedLinkError 使应用程序在 System.loadLibrary() 行中崩溃.java.lang.UnsatisfiedLinkError: 无法从加载器 dalvik.system.PathClassLoader[dexPath=/data/app/app_id-2.apk,libraryPath=/data/app-lib/app_id-2] 加载 stlport_shared:findLibrary返回空

Crashes the app in on of the System.loadLibrary() lines with a java.lang.UnsatisfiedLinkError. java.lang.UnsatisfiedLinkError: Couldn't load stlport_shared from loader dalvik.system.PathClassLoader[dexPath=/data/app/app_id-2.apk,libraryPath=/data/app-lib/app_id-2]: findLibrary returned null

解决方法

我们开始对所有安装运行一些自定义诊断,以检查是否每个库都解压到 /data/data/app_id/lib 文件夹中.

We started running some custom diagnostics on all our installs to check if every lib is unpacked in the /data/data/app_id/lib folder.

PackageManager m = context.getPackageManager();
String s = context.getPackageName();
PackageInfo p;
p = m.getPackageInfo(s, 0);
s = p.applicationInfo.dataDir;

File appDir = new File(s);
long freeSpace = appDir.getFreeSpace();

File[] appDirList = appDir.listFiles();
int numberOfLibFiles = 0;
boolean subFilesLarger0 = true;
for (int i = 0; i < appDirList.length; i++) {

    if(appDirList[i].getName().startsWith("lib")) {
        File[] subFile = appDirList[i].listFiles(FileFilters.FilterDirs);   
        numberOfLibFiles = subFile.length;
        for (int j = 0; j < subFile.length; j++) {
            if(subFile[j].length() <= 0) {
                subFilesLarger0 = false;
                break;
            }
        }
    }
}

在我们拥有 numberOfLibFiles == 3subFilesLarger0 == true 的每部测试手机上.我们想测试是否所有库都正确解包并且大于 0 字节.此外,我们正在查看 freeSpace 以查看有多少可用磁盘空间.freeSpace 与您可以在屏幕底部的设置 --> 应用程序中找到的内存量相匹配.这种方法背后的想法是,当磁盘上没有足够的可用空间时,安装程​​序可能会在解压 APK 时遇到问题.

On every test phone that we have numberOfLibFiles == 3 and subFilesLarger0 == true. We wanted to test if all libs are unpacked properly and are larger then 0 byte. In addition we're looking at freeSpace to see how much disk space is available. freeSpace matches the amount of memory that you can find in Settings --> Applications at the bottom of the screen. The thinking behind this approach was that when there is not enough space on the disk available that the installer might have problems unpacking the APK.

真实世界场景

查看诊断结果,有些设备确实没有/data/data/app_id/lib 文件夹中包含所有 3 个库,但有很多免费的空间.我想知道为什么错误消息正在寻找 /data/app-lib/app_id-2.我们所有的手机都将它们的库存储在 /data/data/app_id/lib 中.System.loadLibrary() 是否应该在安装和加载库时使用一致的路径?我如何知道操作系统在哪里寻找库?

Looking at the diagnostics, some of the devices out there do NOT have all 3 libs in the /data/data/app_id/lib folder but have plenty of free space. I'm wondering why the error message is looking for /data/app-lib/app_id-2. All our phones store their libs in /data/data/app_id/lib. Also the System.loadLibrary() should use a consistent path across installation and loading the libs? How can I know where the OS is looking for the libs?

问题

有人在安装本机库时遇到问题吗?哪些变通方法取得了成功?有没有在不存在时通过互联网下载本机库并手动存储它们的经验?首先可能导致问题的原因是什么?

Anyone experiencing problems with installing native libs? What work arounds have been successful? Any experience with just downloading native libs over the internet when they are not existent and storing them manually? What could cause the problem in the first place?

编辑

我现在也有一个用户在应用程序更新后遇到了这个问题.以前的版本在他的手机上运行良好,更新后本地库似乎丢失了.手动复制库似乎也会造成麻烦.他在 android 4.x 上使用没有自定义 ROM 的非 root 手机.

I now also have a user who runs into this problem after an application update. The previous version worked fine on his phone, an after an update the native libs seem missing. Copying the libs manually seem to cause trouble as well. He is on android 4.x with a non rooted phone without custom ROM.

编辑 2 - 解决方案

在这个问题上花费了 2 年时间.我们想出了一个对我们现在很有效的解决方案.我们开源了它:https://github.com/KeepSafe/ReLinker

After 2 years of spending time on this problem. We came up with a solution that works well for us now. We open sourced it: https://github.com/KeepSafe/ReLinker

推荐答案

我遇到了同样的问题,并且 UnsatisfiedLinkErrors 出现在所有版本的 Android 上 - 在过去 6 个月中,对于当前有超过 90000 次活动安装的应用程序,我有:

I have the same trouble, and the UnsatisfiedLinkErrors comes on all versions of Android - over the past 6 months, for an app that currently has over 90000 active installs, I had:

Android 4.2     36  57.1%
Android 4.1     11  17.5%
Android 4.3     8   12.7%
Android 2.3.x   6   9.5%
Android 4.4     1   1.6%
Android 4.0.x   1   1.6%

并且用户报告说它通常在应用更新后发生.这是针对每天获得大约 200 到 500 个新用户的应用程序.

and the users report that it usually happens just after the app update. This is for an app that gets around 200 - 500 new users per day.

我想我想出了一个更简单的解决方法.我可以通过这个简单的调用找出我的应用程序的原始 apk 在哪里:

I think I came up with a simpler work-around. I can find out where is the original apk of my app with this simple call:

    String apkFileName = context.getApplicationInfo().sourceDir;

这会返回类似于/data/app/com.example.pkgname-3.apk"的内容,即我的应用的 APK 文件的确切文件名.该文件是一个普通的 ZIP 文件,无需 root 即可读取.因此,如果我捕获 java.lang.UnsatisfiedLinkError,我可以从 .apk (zip) lib/armeabi-v7a 文件夹(或我所在的任何架构)内部提取并复制我的本机库,并将其复制到任何目录我可以读/写/执行,并用 System.load(full_path) 加载它.

this returns something like "/data/app/com.example.pkgname-3.apk", the exact file name of my app's APK file. This file is a regular ZIP file and it is readable without root. Therefore, if I catch the java.lang.UnsatisfiedLinkError, I can extract and copy my native library, from the inside of .apk (zip) lib/armeabi-v7a folder (or whatever architecture I'm on), to any directory where I can read/write/execute, and load it with System.load(full_path).

2014 年 7 月 1 日更新 自从 2014 年 6 月 23 日发布了我的产品版本(其代码类似于下面列出的代码)以来,我的本地库中没有任何不满意的链接错误.

Update July 1, 2014 since releasing a version of my product with the code similar to the listed below, on June 23, 2014, did not have any Unsatisfied Link Errors from my native library.

这是我使用的代码:

public static void initNativeLib(Context context) {
    try {
        // Try loading our native lib, see if it works...
        System.loadLibrary("MyNativeLibName");
    } catch (UnsatisfiedLinkError er) {
        ApplicationInfo appInfo = context.getApplicationInfo();
        String libName = "libMyNativeLibName.so";
        String destPath = context.getFilesDir().toString();
        try {
            String soName = destPath + File.separator + libName;
            new File(soName).delete();
            UnzipUtil.extractFile(appInfo.sourceDir, "lib/" + Build.CPU_ABI + "/" + libName, destPath);
            System.load(soName);
        } catch (IOException e) {
            // extractFile to app files dir did not work. Not enough space? Try elsewhere...
            destPath = context.getExternalCacheDir().toString();
            // Note: location on external memory is not secure, everyone can read/write it...
            // However we extract from a "secure" place (our apk) and instantly load it,
            // on each start of the app, this should make it safer.
            String soName = destPath + File.separator + libName;
            new File(soName).delete(); // this copy could be old, or altered by an attack
            try {
                UnzipUtil.extractFile(appInfo.sourceDir, "lib/" + Build.CPU_ABI + "/" + libName, destPath);
                System.load(soName);
            } catch (IOException e2) {
                Log.e(TAG "Exception in InstallInfo.init(): " + e);
                e.printStackTrace();
            }
        }
    }
}

不幸的是,如果一个糟糕的应用更新留下了旧版本的原生库,或者以某种方式留下了副本损坏,我们用 System.loadLibrary("MyNativeLibName") 加载,没有办法卸载它.在发现标准应用程序本机 lib 文件夹中残留的此类已失效库后,例如通过调用我们的一个本地方法并发现它不存在(UnsatisfiedLinkError 再次出现),我们可以存储首选项以避免完全调用标准 System.loadLibrary() 并在下一次应用启动时依赖我们自己的提取和加载代码.

Unfortunately, if a bad app update leaves an old version of the native library, or a copy somehow damaged, which we loaded with System.loadLibrary("MyNativeLibName"), there is no way to unload it. Upon finding out about such remnant defunct library lingering in the standard app native lib folder, e.g. by calling one of our native methods and finding out it's not there (UnsatisfiedLinkError again), we could store a preference to avoid calling the standard System.loadLibrary() altogether and relying on our own extraction and loading code upon next app startups.

为了完整起见,这里是 UnzipUtil 类,我从这个 CodeJava UnzipUtility 文章:

For completeness, here is UnzipUtil class, that I copied and modified from this CodeJava UnzipUtility article:

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class UnzipUtil {
    /**
     * Size of the buffer to read/write data
     */

    private static final int BUFFER_SIZE = 4096;
    /**
     * Extracts a zip file specified by the zipFilePath to a directory specified by
     * destDirectory (will be created if does not exists)
     * @param zipFilePath
     * @param destDirectory
     * @throws java.io.IOException
     */
    public static void unzip(String zipFilePath, String destDirectory) throws IOException {
        File destDir = new File(destDirectory);
        if (!destDir.exists()) {
            destDir.mkdir();
        }
        ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry entry = zipIn.getNextEntry();
        // iterates over entries in the zip file
        while (entry != null) {
            String filePath = destDirectory + File.separator + entry.getName();
            if (!entry.isDirectory()) {
                // if the entry is a file, extracts it
                extractFile(zipIn, filePath);
            } else {
                // if the entry is a directory, make the directory
                File dir = new File(filePath);
                dir.mkdir();
            }
            zipIn.closeEntry();
            entry = zipIn.getNextEntry();
        }
        zipIn.close();
    }

    /**
     * Extracts a file from a zip to specified destination directory.
     * The path of the file inside the zip is discarded, the file is
     * copied directly to the destDirectory.
     * @param zipFilePath - path and file name of a zip file
     * @param inZipFilePath - path and file name inside the zip
     * @param destDirectory - directory to which the file from zip should be extracted, the path part is discarded.
     * @throws java.io.IOException
     */
    public static void extractFile(String zipFilePath, String inZipFilePath, String destDirectory) throws IOException  {
        ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry entry = zipIn.getNextEntry();
        // iterates over entries in the zip file
        while (entry != null) {
            if (!entry.isDirectory() && inZipFilePath.equals(entry.getName())) {
                String filePath = entry.getName();
                int separatorIndex = filePath.lastIndexOf(File.separator);
                if (separatorIndex > -1)
                    filePath = filePath.substring(separatorIndex + 1, filePath.length());
                filePath = destDirectory + File.separator + filePath;
                extractFile(zipIn, filePath);
                break;
            }
            zipIn.closeEntry();
            entry = zipIn.getNextEntry();
        }
        zipIn.close();
    }

    /**
     * Extracts a zip entry (file entry)
     * @param zipIn
     * @param filePath
     * @throws java.io.IOException
     */
    private static void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        byte[] bytesIn = new byte[BUFFER_SIZE];
        int read = 0;
        while ((read = zipIn.read(bytesIn)) != -1) {
            bos.write(bytesIn, 0, read);
        }
        bos.close();
    }
}

格雷格

这篇关于为什么有些 Android 手机会导致我们的应用抛出 java.lang.UnsatisfiedLinkError?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆