此文已由作者尹彬彬授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
0X0 前言
在Android系统中,当我们安装apk文件的时候,lib目录下的so文件会被解压到app的原生库目录,一般来说是放到/data/data/<package-name>/lib目录下,而根据系统和CPU架构的不同,其拷贝策略也是不一样的,在我们测试过程中发现不正确地配置了so文件,比如某些app使用第三方的so时,只配置了其中某一种CPU架构的so,可能会造成app在某些机型上的适配问题。所以这篇文章主要介绍一下在不同版本的Android系统中,安装apk时,PackageManagerService选择解压so库的策略,并给出一些so文件配置的建议。
0x1 Android4.0以前
当apk被安装时,执行路径虽然有差别,但最终要调用到的一个核心函数是copyApk,负责拷贝apk中的资源。
参考2.3.6的android源码,它的copyApk其内部函数一段选取原生库so逻辑:
public static int listPackageNativeBinariesLI(ZipFile zipFile, List> nativeFiles) throws ZipException, IOException { String cpuAbi = Build.CPU_ABI; int result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi, nativeFiles); /* * Some architectures are capable of supporting several CPU ABIs * for example, 'armeabi-v7a' also supports 'armeabi' native code * this is indicated by the definition of the ro.product.cpu.abi2 * system property. * * only scan the package twice in case of ABI mismatch */ if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { final String cpuAbi2 = SystemProperties.get("ro.product.cpu.abi2", null); if (cpuAbi2 != null) { result = listPackageSharedLibsForAbiLI(zipFile, cpuAbi2, nativeFiles); } if (result == PACKAGE_INSTALL_NATIVE_ABI_MISMATCH) { Slog.w(TAG, "Native ABI mismatch from package file"); return PackageManager.INSTALL_FAILED_INVALID_APK; } if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { cpuAbi = cpuAbi2; } } /* * Debuggable packages may have gdbserver embedded, so add it to * the list to the list of items to be extracted (as lib/gdbserver) * into the application's native library directory later. */ if (result == PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES) { listPackageGdbServerLI(zipFile, cpuAbi, nativeFiles); } return PackageManager.INSTALL_SUCCEEDED; }
这段代码中的Build.CPU_ABI和“ro.product.cpu.abi2”分别为手机支持的主abi和次abi属性字符串,abi为手机支持的指令集所代表的字符串,比如armeabi-v7a、armeabi、x86、mips等,而主abi和次abi分别表示手机支持的第一指令集和第二指令集。代码首先调用listPackageSharedLibsForAbiLI来遍历主abi目录。当主abi目录不存在时,才会接着调用listPackageSharedLibsForAbiLI遍历次abi目录。
/* * Find all files of the form lib//lib.so in the .apk * and add them to a list to be installed later. * * NOTE: this method may throw an IOException if the library cannot * be copied to its final destination, e.g. if there isn't enough * room left on the data partition, or a ZipException if the package * file is malformed. */ private static int listPackageSharedLibsForAbiLI(ZipFile zipFile, String cpuAbi, List> libEntries) throws IOException, ZipException { final int cpuAbiLen = cpuAbi.length(); boolean hasNativeLibraries = false; boolean installedNativeLibraries = false; if (DEBUG_NATIVE) { Slog.d(TAG, "Checking " + zipFile.getName() + " for shared libraries of CPU ABI type " + cpuAbi); } Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); // skip directories if (entry.isDirectory()) { continue; } String entryName = entry.getName(); /* * Check that the entry looks like lib//lib.so * here, but don't check the ABI just yet. * * - must be sufficiently long * - must end with LIB_SUFFIX, i.e. ".so" * - must start with APK_LIB, i.e. "lib/" */ if (entryName.length() < MIN_ENTRY_LENGTH || !entryName.endsWith(LIB_SUFFIX) || !entryName.startsWith(APK_LIB)) { continue; } // file name must start with LIB_PREFIX, i.e. "lib" int lastSlash = entryName.lastIndexOf('/'); if (lastSlash < 0 || !entryName.regionMatches(lastSlash + 1, LIB_PREFIX, 0, LIB_PREFIX_LENGTH)) { continue; } hasNativeLibraries = true; // check the cpuAbi now, between lib/ and /lib.so if (lastSlash != APK_LIB_LENGTH + cpuAbiLen || !entryName.regionMatches(APK_LIB_LENGTH, cpuAbi, 0, cpuAbiLen)) continue; /* * Extract the library file name, ensure