Android 15.0 ROM定制:无源码App图标替换指南
2025-08-31 / 龙之叶   

1. 概述

在Android 15.0系统产品ROM定制化开发过程中,部分产品需求要求替换Launcher3中显示的app图标。对于无源码的app,这一操作需通过修改PMS(PackageManagerService)在解析app时的行为来实现。具体来说,需在解析过程中替换app的icon资源。

2. 无源码app修改icon图标的相关核心类

核心类位于:

1
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()方法

1
2
3
4
5
6
7
8
@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()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()方法

1
2
3
4
5
6
7
8
9
@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()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()方法中,首先获取ApplicationInfopkgName,然后解析application的属性。其中,ai.iconRes即为app的图标资源ID。因此,要修改app的图标,只需修改此属性即可。

本文链接:
http://longzhiye.top/2025/08/31/2025-08-31/