一.scanPackageLI
PKMS 中調用scanDirLI來分析APK 文件,如果目錄下的是apk文件或者是目錄,會繼續調用scanPackageLI函數:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
... ...
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
... ...
}
這段代碼主要作用是調用了PackageParser的parsePackage來解析文件,返回Package;
二.PackageParser
PackageParser.parserPackage
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
// 目錄
return parseClusterPackage(packageFile, flags);
} else {
//單個APK
return parseMonolithicPackage(packageFile, flags);
}
}
parseClusterPackage
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);//獲取應用目錄的PackageLite對象,這個對象分開保存了目錄下的核心應用以及非核心應用的名稱
if (mOnlyCoreApps && !lite.coreApp) {//如果lite中沒有核心應用,退出
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
final AssetManager assets = new AssetManager();
try {
// Load the base and all splits into the AssetManager
// so that resources can be overriden when parsing the manifests.
loadApkIntoAssetManager(assets, lite.baseCodePath, flags);//裝載核心應用的資源
if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {
for (String path : lite.splitCodePaths) {
loadApkIntoAssetManager(assets, path, flags);//再裝載非核心應用的資源
}
}
final File baseApk = new File(lite.baseCodePath);
final Package pkg = parseBaseApk(baseApk, assets, flags);//對核心應用解析
... ...
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
for (int i = 0; i < num; i++) {
parseSplitApk(pkg, i, assets, flags);//對非核心應用的處理
}
}
pkg.codePath = packageDir.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
parseClusterPackage的主要內容,就是用於解析存在多個APK的文件的Package:
- 1. 調用parseClusterPackageLite解析目錄下的多APK文件,獲取對應的PackageLite對象lite;
- 2. 創建AssetManager對象,並調用loadApkIntoAssetManager方法載入"base APK"
- 3. 調用parseBaseApk方法獲取對應的Package對象
- 4. 遍歷所有"拆分APK",然後載入第二步創建的AssetManager對象,這樣就實現了資源文件的載入
parseMonolithicPackage
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//如果是核心應用則以更輕量級的方式進行解析後,判斷是否是核心應用,非核心應用不執行解析過程
if (mOnlyCoreApps) {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AssetManager assets = new AssetManager();
try {
//調用parseBaseAPK()繼續解析操作
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
解析操作繼續由parseBaseApk(apkFile, assets, flags)
完成,parseBaseApk(apkFile, assets, flags)最終調用同名函數:
parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError)
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
//....省略多行代碼....
//循環解析AndroidManifest.xml中的元素
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (tagName.equals("application")) {
//....省略多行代碼,重點關注其中調用的parseBaseApplication()方法
}else if (tagName.equals("overlay")) {
//....省略多行代碼....
}else if (tagName.equals("key-sets")){
//paseKeySets()
}else if (tagName.equals("permission-group")) {
parsePermissionGroup(pkg, flags, res, parser, attrs,
}else if (tagName.equals("permission")) {
//parsePermission
}else if (tagName.equals("uses-configuration")) {
//....省略多行代碼....
}else if (tagName.equals("uses-feature")) {
//parseUsesFeature()
}else if (tagName.equals("feature-group")) {
//....省略多行代碼....
}else if (tagName.equals("uses-sdk")) {
//....省略多行代碼....
}else if (tagName.equals("supports-screens")) {
//....省略多行代碼....
}else if (tagName.equals("protected-broadcast")) {
//....省略多行代碼....
}else if (tagName.equals("instrumentation")) {
//....省略多行代碼....
}else if (tagName.equals("original-package")) {
//....省略多行代碼....
}else if (tagName.equals("adopt-permissions")) {
//....省略多行代碼....
}
//....省略多行代碼....
}
//....省略多汗代碼....
}
此函數就是解析APK 的AndroidManifest.xml 文件;
所以,PackageParser.parsePackage() 函數的作用可以簡單的看成完成了 dir 下對應APK 的AndroidManifest.xml 的解析,保存在package 對象中並返回;
- scanPackageLI(File scanFile,...)該函數負責完成APK的掃描工作,APK的掃描工作具體有PackageParser.parsePackage(*)完成。在掃描過程中,會解析AndroidManifiest.xml文件等信息,並將掃描結果存放在PackageParser.Package對象中。
- 在完成包的信息解析之後需要完成包信息同步工作,主要因爲:scanPackageLI掃描到的APK可能是已經更名的包、disable的包、需要升級的包、已經安裝並且簽名衝突的包、被非系統級包替代系統包的情況,需要對這些情況一一處理,保證信息的正確性。
- 如果Package需要Rename或者Update則會進行簽名比較,以防止簽名不一致的情況;
- 最終會調用scanPackageLI(PackageParser.Package,....)函數完成實際的Package安裝或更新操作。
- 在掃描過程中,Package的Rename、Update、Install操作都是由scanPackage(PackageParser,int,int,long,UserHandler)來完成.該函數會調用scanPackageDirtyLI(...)完成具體的操作
scanPackageDirtyLI
需要注意的是:不論是開機掃描安裝APK,還是通過adb命令安裝APK,最終都會調用scanPackageDirtyLI函數進行APK安裝
總結一下:
1 .任一時刻,系統中安裝過的apk都會被解析完成,包信息被存放在PackageManagerService中的mPackages,它是以包名爲Key,以PackageParser.Package爲Value的HashMap。
2 .PMS中scanPackageLI()開機掃描已安裝的apk,installPackageLI()安裝新apk,最終都會走到PackageParser.parseBaseApplication()解析應用manifest文件,在scanPackageDirtyLI()中添加到mPackages.其它地方獲取ApplicationInfo實例,都會間接地調用到PackageParser.generateApplicationInfo().