在使用命令行打包apk时,由于命令行打包直接将resources.arsc进行压缩,会导致这个问题出现,从而在android11及Android12版本的机型上无法安装,提示信息为:解析安装包出错,或者是Targeting R+(version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary]错误
在Android 11以及Android12 版本,对resources.arsc文件需要进行对齐,并且不压缩,
源码路径为 framework/base/core/java - android.content.pm.parsing.ParsingPackageUtils
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final AssetManager assets = assetLoader.getBaseAssetManager();
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags);
if (result.isError()) {
return input.error(result.getErrorCode(),
apkPath + " (at " + parser.getPositionDescription() + "): "
+ result.getErrorMessage());
}
final ParsingPackage pkg = result.getResult();
//判断resources.arsc文件是否被压缩且是否用zipalign对齐过,如果资源resources.arsc被压缩了,或者没有用zipalign对齐,那么就会抛出这
//个异常
if (assets.containsAllocatedTable()) {
final ParseResult<?> deferResult = input.deferError(
"Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
+ " the resources.arsc of installed APKs to be stored uncompressed"
+ " and aligned on a 4-byte boundary",
DeferredError.RESOURCES_ARSC_COMPRESSED);
if (deferResult.isError()) {
return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
deferResult.getErrorMessage());
}
}
ApkAssets apkAssets = assetLoader.getBaseApkAssets();
boolean definesOverlayable = false;
try {
definesOverlayable = apkAssets.definesOverlayable();
} catch (IOException ignored) {
// Will fail if there's no packages in the ApkAssets, which can be treated as false
}
if (definesOverlayable) {
SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
int size = packageNames.size();
for (int index = 0; index < size; index++) {
String packageName = packageNames.valueAt(index);
Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
for (String overlayable : overlayableToActor.keySet()) {
pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
}
}
}
}
pkg.setVolumeUuid(volumeUuid);
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
pkg.setSigningDetails(getSigningDetails(pkg, false));
} else {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
}
}
/**
* Returns whether the {@code resources.arsc} of any loaded apk assets is allocated in RAM
* (not mmapped).
*
* @hide
*/
public boolean containsAllocatedTable() {
synchronized (this) {
ensureValidLocked();
return nativeContainsAllocatedTable(mObject);
}
}
所以,如果是使用命令行去打包,且target是Android11以后的版本,那么在安装到Android11以及Android12机型,就会出现以上的问题。
解决方法是,在压缩的使用,判断是否是resources.arsc ,则不进行压缩,使用ZipEntry.STORED方式保存
if ("resources.arsc".equals(file.getName())){
entry.setMethod(ZipEntry.STORED);
entry.setSize(file.length());
entry.setCompressedSize(-1);
BufferedInputStream unknownFile = new BufferedInputStream(new FileInputStream(file));
CRC32 crc = calculateCrc(unknownFile);
entry.setCrc(crc.getValue());
}