1. 概述
在Android 15.0系统产品ROM定制化开发过程中,部分产品需求要求替换Launcher3中显示的app图标。对于无源码的app,这一操作需通过修改PMS(PackageManagerService)在解析app时的行为来实现。具体来说,需在解析过程中替换app的icon资源。
2. 无源码app修改icon图标的相关核心类
核心类位于:
frameworks/base/core/java/android/content/pm/PackageParser.java
3. 无源码app修改icon图标的核心功能实现与分析
3.1 PMS(PackageManagerService)概述
PMS是Android提供的包管理系统服务,负责管理所有包的信息,包括应用的安装、卸载、更新以及解析AndroidManifest.xml文件。PMS通过解析每个安装应用的AndroidManifest.xml文件,保存其中的数据,并为AMS(ActivityManagerService)提供所需数据。
3.2 解析app的方法分析
在安装过程中,PMS首先遍历/data/app和/system/app文件夹,找到apk文件,然后通过submit()方法进行apk的解析。解析时,将apk文件路径传入PackageParser对象的parsePackage()方法进行解析。不同系统源码版本的解析方式可能有所不同。
3.2.1 parsePackage()方法
@UnsupportedAppUsage
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
在解析app时,PMS会优先调用parsePackage(File packageFile, int flags, boolean useCaches)方法(尽管示例中未直接展示,但根据描述可推断),进而调用parseMonolithicPackage(File apkFile, int flags)方法进行app资源的解析。
3.2.2 parseMonolithicPackage()方法
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}
在parseMonolithicPackage()方法中,会调用parseBaseApk()方法进一步解析app。
3.2.3 parseBaseApk()方法
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
// 解析app的基础信息,包括版本号、编译SDK版本等
final String pkgName;
final Package pkg = new Package(pkgName);
// ... 其他解析代码 ...
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
parseBaseApk()方法最终会调用parseBaseApkCommon()方法继续解析app的xml内容。当解析到TAG_APPLICATION标签时,会调用parseBaseApplication()方法解析application的相关内容。
3.2.4 parseBaseApplication()方法
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
// 解析application的icon资源
ai.iconRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
ai.roundIconRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_roundIcon, 0);
// 示例:修改特定app的icon(以com.sprd.sprdnote为例)
if (!TextUtils.isEmpty(pkgName) && pkgName.equals("com.sprd.sprdnote")) {
ai.iconRes = com.android.internal.R.drawable.ic_battery;
}
// ... 其他解析代码 ...
}
在parseBaseApplication()方法中,首先获取ApplicationInfo的pkgName,然后解析application的属性。其中,ai.iconRes即为app的图标资源ID。因此,要修改app的图标,只需修改此属性即可。














网友评论