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

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

问题描述

我们正在经历一个 java.lang.UnsatisfiedLinkError中的一些是在市场上使用我们的应用程序的Andr​​oid手机。

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"); 
}

崩溃的应用程序上的的System.loadLibrary()行与 java.lang.UnsatisfiedLinkError中 java.lang.UnsatisfiedLinkError中:无法加载从装载机dalvik.system.PathClassLoader [dexPath = /数据/应用/ APP_ID-2.apk,LIBRARYPATH = /数据/应用-LIB / APP_ID-2 stlport_shared ]:findLibrary返回null

解决方法

我们开始运行在所有安装一些自定义的诊断检查,如果每个LIB解包在 /数据/数据​​/ 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 == 3 subFilesL​​arger0 ==真。我们想测试,如果所有的库都正确解包和更大然后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.

真实的场景

综观诊断,一些设备在那里做的不可以拥有所有3个库中的 /数据/数据​​/ APP_ID / lib目录文件夹,但有足够的自由空间。我不知道为什么错误消息正在寻找 /数据/应用-LIB / APP_ID-2 。我们所有的手机存储的库在 /数据/数据​​/ 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?

修改

我现在也有谁的应用程序更新后运行到这个问题的用户。在previous版本精细的工作对他的电话,更新本机库似乎失踪后。复制库手动似乎带来麻烦为好。他是在Android 4.x版与非植根电话没有定制ROM。

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来自于所有版本的Andr​​oid - 在过去的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文件,它是可读的无根。因此,如果我赶上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日,因为发布一个版本,我的产品与code类似下面的上市,6月23日,2014年,并没有从我的家乡任何不合适的链接错误库。

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.

下面是code我用:

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一次),我们可以存储一个preference完全避免调用标准的System.loadLibrary()并在依靠我们自己的提取和加载code下一个应用程序启动。

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类,我复制并从该<修改的href="http://www.$c$cjava.net/java-se/file-io/programmatically-extract-a-zip-file-using-java">$c$cJava 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天全站免登陆