我也遇到了同样的问题,并且 UnsatisfiedLinkErrors 出现在所有版本的 Android 上 - 在过去的 6 个月中,对于当前拥有超过 90000 次活动安装的应用程序,我有:
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 个新用户的应用程序。
我想我想出了一个更简单的解决方法。我可以通过这个简单的调用找出我的应用程序的原始 apk 在哪里:
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) 加载它。
编辑:它似乎工作
自 2014 年 6 月 23 日发布我的产品版本以来,2014 年 7 月 1 日更新,代码类似于下面列出的代码,我的本地库中没有任何不满意的链接错误。
这是我使用的代码:
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( ) 完全依赖于我们自己的提取和加载代码在下一次应用程序启动时。
为了完整起见,这里是 UnzipUtil 类,我从这篇CodeJava UnzipUtility 文章中复制并修改了它:
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();
}
}
格雷格