APK安裝流程系列文章整體內容如下:
- APK安裝流程詳解0——前言
- APK安裝流程詳解1——有關"安裝ing"的實體類概述
- APK安裝流程詳解2——PackageManager簡介
- APK安裝流程詳解3——PackageManager與PackageManagerService
- APK安裝流程詳解4——安裝中關于so庫的哪些事
- APK安裝流程詳解5——PackageInstallerService和Installer
- APK安裝流程詳解6——PackageManagerService啟動前奏
- APK安裝流程詳解7——PackageManagerService的啟動流程(上)
- APK安裝流程詳解8——PackageManagerService的啟動流程(下)
- APK安裝流程詳解9——PackageParser解析APK(上)
- APK安裝流程詳解10——PackageParser解析APK(下)
- APK安裝流程詳解11——普通應用安裝簡介
- APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
- APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)
- APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充
- APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補充
- APK安裝流程詳解16——Android包管理總結(尚未完結請期待)
本片文章的主要內容如下:
- 12、PackageParse#parseBaseApplication(Package, Resources,XmlPullParser, AttributeSet, int, String[])方法解析
- 13、PackageParse#parseClusterPackage(File,int)方法解析
- 14、PackageParse#parseClusterPackageLite(File,int)方法解析
- 15、PackageParse#loadApkIntoAssetManager(AssetManager, String, int)方法解析
- 16、PackageParse#parseSplitApk(Package, int, AssetManager, int)方法解析
- 17、PackageParse#parseSplitApk(Package, Resources, XmlResourceParser, int,int, String[])方法解析
- 18、PackageParse#parseSplitApplication(Package, Resources, XmlResourceParser, int, int, String[]) 方法解析
- 19、PackageParse#parseActivity(Package, Resources,XmlPullParser, AttributeSet, int, String[],boolean, boolean)方法解析
- 20、總結
十二、PackageParse#parseBaseApplication(Package, Resources,XmlPullParser, AttributeSet, int, String[])方法解析
代碼在PackageParser.java 2406行
/**
* Parse the {@code application} XML tree at the current parse location in a
* <em>base APK</em> manifest.
* <p>
* When adding new features, carefully consider if they should also be
* supported by split APKs.
*/
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
// 獲取ApplicationInfo對象ai
final ApplicationInfo ai = owner.applicationInfo;
// 獲取包名
final String pkgName = owner.applicationInfo.packageName;
// 從資源里面獲取AndroidManifest的數組
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestApplication);
// 獲取Application的名字
String name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_name, 0);
if (name != null) {
// 如果有設置過Application,則設置ApplicationInfo的類名
ai.className = buildClassName(pkgName, name, outError);
if (ai.className == null) {
// 如果設置過Application則直接返回
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
// 在AndroidManifest里面是否設置了android:manageSpaceActivity屬性,如果設置了則manageSpaceActivity不為空,沒有設置manageSpaceActivity為空
String manageSpaceActivity = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity,
Configuration.NATIVE_CONFIG_VERSION);
if (manageSpaceActivity != null) {
// 如果設置了,則添加類名
ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
outError);
}
// 是否設置了androidMannifest.xml文件中android:allowBackup屬性;
boolean allowBackup = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
// 如果設置了 允許備份
if (allowBackup) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
// backupAgent, killAfterRestore, fullBackupContent and restoreAnyVersion are only
// relevant if backup is possible for the given application.
// 獲取backupAgent,如果在AndroidManifest里面設置了android:backupAgent屬性,則backupAgent不為空,否則backupAgent為空
String backupAgent = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent,
Configuration.NATIVE_CONFIG_VERSION);
if (backupAgent != null) {
// 設置了backupAgent,這構建類名
ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
if (DEBUG_BACKUP) {
Slog.v(TAG, "android:backupAgent = " + ai.backupAgentName
+ " from " + pkgName + "+" + backupAgent);
}
//是否設置了killAfterRestore屬性,即在AndroidManfest中是否設置了android:killAfterRestore=true,如果設置了,配置相應標志
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
true)) {
ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
}
//是否設置了restoreAnyVersion屬性,即在AndroidManfest中是否設置了restoreAnyVersion=boolean,如果設置了,配置相應標志
// 這里restoreAnyVersion屬性 是指是否允許回復任意版本的本分數據來恢復應用程序的數據。
//將該屬性設置為true,則將允許本分管理器嘗試恢復操作,有的時候版本不匹配表明數據是不兼容的,
// 這個時候如果可以恢復到不同的版本的數據,那么應用程序將承受很大風險,所以請謹慎使用此屬性。
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion,
false)) {
ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
}
// 是否開啟[Auto Backup for Apps](https://developer.android.com/guide/topics/data/autobackup.html)功能
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly,
false)) {
ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
}
}
// 獲取android:fullBackupContent的屬性,這個標示用來指明備份數據的規則,該標示是配合[Auto Backup for Apps](https://developer.android.com/guide/topics/data/autobackup.html)來使用的
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupContent);
if (v != null && (ai.fullBackupContent = v.resourceId) == 0) {
if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent specified as boolean=" +
(v.data == 0 ? "false" : "true"));
}
// "false" => -1, "true" => 0
ai.fullBackupContent = (v.data == 0 ? -1 : 0);
}
if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent=" + ai.fullBackupContent + " for " + pkgName);
}
}
// 獲取<Application> 里面的"label"屬性,并設置相應的屬性
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestApplication_label);
if (v != null && (ai.labelRes=v.resourceId) == 0) {
ai.nonLocalizedLabel = v.coerceToString();
}
// 設置icon、logo、banner、theme、descriptionRes屬性
ai.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
ai.logo = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
ai.banner = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_banner, 0);
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
// 判斷是否是系統APP
if ((flags&PARSE_IS_SYSTEM) != 0) {
// 判斷是否長期駐留
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_persistent,
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
}
// 獲取應用的android:requiredForAllUsers屬性,是否設置該應用是否需要對所有用戶可用
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
false)) {
owner.mRequiredForAllUsers = true;
}
// 設置android:restrictedAccountType屬性
// 是否允許受限用戶訪問機主的該賬戶,如果應用程序要使用Account 并且允許受限用戶訪問主賬戶
// 本屬性值必須與應用程序賬戶認證類型(由AuthenticatorDescription)定義吻合。
// 如果設置了本屬性將允許受限用戶通過主賬戶使用你的應用程序,這可能會泄露個人身份信息。
String restrictedAccountType = sa.getString(com.android.internal.R.styleable
.AndroidManifestApplication_restrictedAccountType);
if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
owner.mRestrictedAccountType = restrictedAccountType;
}
// 設置 android:restrictedAccountType中的值
// 設置應用程序所需的賬戶類型。如果應用程序需要一個Account才能運行,本屬性必須與賬戶認證類型(由AuthenticatorDescription 定義)吻合。
String requiredAccountType = sa.getString(com.android.internal.R.styleable
.AndroidManifestApplication_requiredAccountType);
if (requiredAccountType != null && requiredAccountType.length() > 0) {
owner.mRequiredAccountType = requiredAccountType;
}
// 是否設置了 android:debuggable
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
false)) {
ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
}
// 是否設置了 android:vmSafeMode屬性
// 這個表示用來指明這個應用是否想讓 VM虛擬機運行在安全模式,默認值為false
//這個標示是API 18版本添加,如果設置 true 將會禁用 Dalvik just-in-time(JIT)編譯器,
// 這個標示在 API 22 版本之后為新版本做了改進,因為4.4 之后 Dalvik 虛擬機就被廢棄
// 在22版本 之后這個標示如果設置 為true 將會禁用ART ahead-of-time(AOT) 編譯器
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_vmSafeMode,
false)) {
ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
}
// 設置 是否硬件加速
owner.baseHardwareAccelerated = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
if (owner.baseHardwareAccelerated) {
ai.flags |= ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
}
// 是否包含代碼 對應AndroidManifest里面的android:hasCode
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
true)) {
ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
}
// 是否設置了"android:allowTaskReparenting=boolean",這個標示和Application 的 標示意義一樣,
// 所以如果同時聲明該標示,這個標示會覆蓋Application 的標示
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
false)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
}
// 是否設置了android:allowClearUserData=boolean。是否給用戶刪除用戶數據的權限,
// 如果為true應用管理者就擁有了清楚數據的權限;false沒有。默認為true
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData,
true)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
}
// 是否設置"android:testOnly=boolean",這個標示用來指明這個應用是不是僅僅作為測試的用戶,
// 比如本應用程序可能會暴露一些不屬于自己的功能或數據,這將引發安全漏洞,
// 但對測試而言這又非常有用,而且這種應用程序只能通過 adb 進行安全
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
false)) {
ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
}
// 對應"android:largeHeap=boolean"屬性,這個標示用來表明這個應用的進程是否需要更大的運行內存空間,這個標示對該應用的所有進程有效
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_largeHeap,
false)) {
ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;
}
// 對應"android:usesCleartextTraffic=boolean"屬性。默認值為true
// 它用來指明應用是否需要使用明文的網絡連接,例如明文的HTTP連接
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
true)) {
ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
}
// 對應"android:supportsRtl=boolean",這個標示是用來聲明應用是否支持從右到左的布局方式
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
false /* default is no RTL support*/)) {
ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
}
// 是否支持多平臺
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_multiArch,
false)) {
ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
}
// 對應AndroidManifest的"android:extractNativeLibs=boolean",這個標示是Android 6.0引入。
// 該屬性如果設置了 false,則系統在安裝系統的時候不會把so文件從apk中解壓出來了
// 同時修改了System.loadLibrary 直接打開調用apk中的.so文件。但是,目前要讓該熟悉感生效還有兩個條件:
// 一是apk中的.so文件不能被壓縮;二是.so必須是用zipalign -p 4來對齊。該標示的默認值為true。
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
true)) {
ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
}
String str;
// 對應"android:permission=string"屬性
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
ai.permission = (str != null && str.length() > 0) ? str.intern() : null;
// 解析"android:allowTaskReparenting=boolean" 設置吸附質,影響Activity的啟動模式
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
str = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
}
// 設置吸附質
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
str, outError);
if (outError[0] == null) {
CharSequence pname;
// 對應"android:process"屬性。
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_process,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
pname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_process);
}
// 設置進程名
ai.processName = buildProcessName(ai.packageName, null, pname,
flags, mSeparateProcesses, outError);
android:enabled
// 對應"android:enable=boolean"屬性,這個標識用來表明系統能否實例化這個應用的組件。
ai.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
// 對應 "android:isGame=boolean" 屬性,這個標識用來表明應用是否是游戲,這樣就能夠將該應用和其他應用分離開,默認值為false
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_isGame, false)) {
ai.flags |= ApplicationInfo.FLAG_IS_GAME;
}
// 這里注意,if(false) 是一定不執行的
if (false) {
// 對應的android:cantSaveState屬性, 設置了則APP就可視為heavy-weight process
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
false)) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
// A heavy-weight application can not be in a custom process.
// We can do direct compare because we intern all strings.
if (ai.processName != null && ai.processName != ai.packageName) {
outError[0] = "cantSaveState applications can not use custom processes";
}
}
}
}
// 對應"android:uiOptions",標識分離式操作欄
ai.uiOptions = sa.getInt(
com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0);
// 回收
sa.recycle();
if (outError[0] != null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
final int innerDepth = parser.getDepth();
int type;
// 開始解析
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
// 解析activity
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
// 解析receiver
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
// 解析service
Service s = parseService(owner, res, parser, attrs, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
// 解析provider
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
// 解析 activity-alias
Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
// 解析meta-data
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("library")) {
// 解析<library>標簽
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestLibrary_name);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (!ArrayUtils.contains(owner.libraryNames, lname)) {
owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-library")) {
// 解析<uses-library>標簽
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
boolean req = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
true);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (req) {
owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
} else {
owner.usesOptionalLibraries = ArrayUtils.add(
owner.usesOptionalLibraries, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-package")) {
// 解析<uses-package>標簽
// Dependencies for app installers; we don't currently try to
// enforce this.
XmlUtils.skipCurrentTag(parser);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <application>: " + tagName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
modifySharedLibrariesForBackwardCompatibility(owner);
// 檢查IntentFilter之一是否包含DEFAULT / VIEW和HTTP / HTTPS數據URI
if (hasDomainURLs(owner)) {
owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
} else {
owner.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
}
return true;
}
先來翻譯下注釋:
解析"base APK" 的manifest的XML樹中
添加新特性的時候,請仔細思考是否支持"拆分APK"。
其實這個方法就是解析了application節點下的所有信息,比如activity、service、receiver、provider、library、users-librayry等信息,同時將解析后的每一個屬性生成相應的對象,添加到傳入的package里面,這些信息最后都會在PackageManagerService中用到。
十三、PackageParse#parseClusterPackage(File,int)方法解析
代碼在PackageParser.java 769行
/**
* Parse all APKs contained in the given directory, treating them as a
* single package. This also performs sanity checking, such as requiring
* identical package name and version codes, a single base APK, and unique
* split names.
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in {@link #collectCertificates(Package, int)}.
*/
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
// ************ 第一步 *************
// 解析目錄,并獲取對應的PackageLite
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
// 核心應用的判斷
if (mOnlyCoreApps && !lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
// ************ 第二步 *************
// 獲取AssetManager對象
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.
// 載入AssetManager到base APK中
loadApkIntoAssetManager(assets, lite.baseCodePath, flags);
// 把AssetManager 載入到每個"拆分APK"中
if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {
for (String path : lite.splitCodePaths) {
loadApkIntoAssetManager(assets, path, flags);
}
}
// ************ 第三步 *************
final File baseApk = new File(lite.baseCodePath);
// 開始解析"base" APK文件,并獲得對應的Package 對象
final Package pkg = parseBaseApk(baseApk, assets, flags);
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
// ************ 第四步 *************
// 開始解析"拆分APK"。
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];
pkg.splitPrivateFlags = new int[num];
for (int i = 0; i < num; i++) {
parseSplitApk(pkg, i, assets, flags);
}
}
// 設置相應屬性
pkg.setCodePath(packageDir.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
// 關閉
IoUtils.closeQuietly(assets);
}
}
該方法有注釋,先來看下方法的注釋:
將目錄視為一個單獨的APK安裝包,解析這個目錄下的所有APK安裝包。同樣也執行例行檢查,比如檢查"base APK"和"拆分APK"是否有相同的安裝包包名和版本號。
注意:這個方法執行簽名驗證,所以要單獨的調用collectCertificates(Package,int)方法
這個方法我的理解:
parseClusterPackage的主要內容,就是用于解析存在多個APK的文件的Package。我將本方法分為四個步驟:
- 第一步:調用parseClusterPackageLite解析目錄下的多APK文件,獲取對應的PackageLite對象lite
- 第二步:創建AssetManager對象,并調用loadApkIntoAssetManager方法載入"base APK"。?
- 第三步:調用parseBaseApk方法獲取對應的Package對象
- 第四步:遍歷所有"拆分APK",然后載入第二步創建的AssetManager對象,這樣就實現了資源文件的載入。
這里面涉及到了幾個方法:
- parseBaseApk(File, AssetManager, int)方法
- parseClusterPackageLite(File,int)方法
- loadApkIntoAssetManager(AssetManager,String,int)方法
- parseSplitApk(Package, int, AssetManager, int)方法
其中第一個parseBaseApk(File, AssetManager, int)方法,已經講解過了,請參考APK安裝流程詳解9——PackageParser解析APK(上)中 五、PackageParse#parseMonolithicPackage(File, int)方法解析
下面我們依次講解下上面的其他三個方法
十四、PackageParse#parseClusterPackageLite(File,int)方法解析
代碼在PackageParser.java 664行
private static PackageLite parseClusterPackageLite(File packageDir, int flags)
throws PackageParserException {
// ************** 第一步 **************
// 獲取目錄下的所有文件
final File[] files = packageDir.listFiles();
// 非空判斷
if (ArrayUtils.isEmpty(files)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
// 初始化
String packageName = null;
int versionCode = 0;
// 開始驗證包名和版本號是否一致
final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
// 遍歷所有文件
for (File file : files) {
if (isApkFile(file)) {
//解析單個APK文件成ApkLite對象
final ApkLite lite = parseApkLite(file, flags);
// Assert that all package names and version codes are
// consistent with the first one we encounter.
// 遍歷的時候只有第一個文件的時候packageName為null
if (packageName == null) {
packageName = lite.packageName;
versionCode = lite.versionCode;
} else {
// 對比當前的lite對象和上一個lite對象的包名是否一致
if (!packageName.equals(lite.packageName)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent package " + lite.packageName + " in " + file
+ "; expected " + packageName);
}
// 對比當前的lite對象和上一個lite對象的版本號是否一致
if (versionCode != lite.versionCode) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent version " + lite.versionCode + " in " + file
+ "; expected " + versionCode);
}
}
// Assert that each split is defined only once
// 保證不重復添加
if (apks.put(lite.splitName, lite) != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Split name " + lite.splitName
+ " defined more than once; most recent was " + file);
}
}
}
// ************** 第二步 **************
// 獲取base APK
final ApkLite baseApk = apks.remove(null);
if (baseApk == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
}
// Always apply deterministic ordering based on splitName
final int size = apks.size();
String[] splitNames = null;
String[] splitCodePaths = null;
int[] splitRevisionCodes = null;
if (size > 0) {
splitNames = new String[size];
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
// 初始化 splitNames
splitNames = apks.keySet().toArray(splitNames);
// splitNames排序
Arrays.sort(splitNames, sSplitNameComparator);
// 初始化splitCodePaths和splitRevisionCodes
for (int i = 0; i < size; i++) {
splitCodePaths[i] = apks.get(splitNames[i]).codePath;
splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode;
}
}
final String codePath = packageDir.getAbsolutePath();
// ************** 第三步 **************
return new PackageLite(codePath, baseApk, splitNames, splitCodePaths,
splitRevisionCodes);
}
我將這個方法分為3個步驟:
- 第一步:理性校驗,主要是校驗包名和版本號是否一致
- 第二步:給baseApk、splitNames、splitCodePaths、splitRevisionCodes、codePath完成初始化
- 第三步:根據第二步給出的變量,new一個PackageLite對象
十五、PackageParse#loadApkIntoAssetManager(AssetManager, String, int)方法解析
代碼在PackageParser.java 846行
private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
throws PackageParserException {
// 簡單的檢查
if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Invalid package file: " + apkPath);
}
// The AssetManager guarantees uniqueness for asset paths, so if this asset path
// already exists in the AssetManager, addAssetPath will only return the cookie
// assigned to it.
// 添加路徑
// **************** 核心方法 ********************
int cookie = assets.addAssetPath(apkPath);
if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
return cookie;
}
這個方法主要讓AssetManager和安裝包的路徑的關聯,主要是調用AssetManager的addAssetPath方法進行關聯。
這里大家重點關注下 addAssetPath 方法,在Android系統中安裝包路徑和AssetManager的關聯是使用AssetManager#addAssetPath(String)
十六、PackageParse#parseSplitApk(Package, int, AssetManager, int)方法解析
代碼在PackageParser.java 913行
private void parseSplitApk(Package pkg, int splitIndex, AssetManager assets, int flags)
throws PackageParserException {
// 獲取路徑
final String apkPath = pkg.splitCodePaths[splitIndex];
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkPath;
if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
// 關聯AssetManager與路徑
final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
// 獲取資源
Resources res = null;
XmlResourceParser parser = null;
try {
// 初始化資源
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
// 初始化Xml解析器
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
// 解析"拆分APK"
pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
// 解析失敗
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
}
}
這個方法不復雜,主要為了調用parseSplitApk(Package, Resources, XmlResourceParser, int,int, String[])方法而初始化相關參數而已。
那我們來看下parseSplitApk(Package, Resources, XmlResourceParser, int,int, String[])方法
十七、PackageParse#parseSplitApk(Package, Resources, XmlResourceParser, int,int, String[])方法解析
代碼在PackageParser.java 957行
/**
* Parse the manifest of a <em>split APK</em>.
* <p>
* Note that split APKs have many more restrictions on what they're capable
* of doing, so many valid features of a base APK have been carefully
* omitted here.
*/
private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser parser, int flags,
int splitIndex, String[] outError) throws XmlPullParserException, IOException,
PackageParserException {
AttributeSet attrs = parser;
// We parsed manifest tag earlier; just skip past it
// 預解析
parsePackageSplitNames(parser, attrs);
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
int type;
boolean foundApp = false;
// 解析 AndroidManifest里面的<Application> 標簽
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
//遇到<application> 標簽
if (tagName.equals("application")) {
// 保證只有一個<application> 標簽
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = "<manifest> has more than one <application>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
XmlUtils.skipCurrentTag(parser);
continue;
}
}
foundApp = true;
// 解析 <application> 標簽
if (!parseSplitApplication(pkg, res, parser, flags, splitIndex, outError)) {
return null;
}
} else if (RIGID_PARSER) {
outError[0] = "Bad element under <manifest>: "
+ parser.getName();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
if (!foundApp) {
outError[0] = "<manifest> does not contain an <application>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
}
return pkg;
}
有注釋,先來翻譯一下注釋:
解析"拆分APK"的manifest
注意:由于對"拆分APK"限制比較多,所以像"base APK"的很多功能在"拆分APK"中已經省略了。
這個方法內部主要是解析"拆分APK"的AndroidManifest 文件,如果遇到<application> 標簽,則調用parseSplitApplication(Package, Resources, XmlResourceParser, int, int, String[]) 方法進行解析
那我們就來看下這個方法
十八、PackageParse#parseSplitApplication(Package, Resources, XmlResourceParser, int, int, String[]) 方法解析
代碼在PackageParser.java 2854行
/**
* Parse the {@code application} XML tree at the current parse location in a
* <em>split APK</em> manifest.
* <p>
* Note that split APKs have many more restrictions on what they're capable
* of doing, so many valid features of a base APK have been carefully
* omitted here.
*/
private boolean parseSplitApplication(Package owner, Resources res, XmlResourceParser parser,
int flags, int splitIndex, String[] outError)
throws XmlPullParserException, IOException {
// 獲取TypedArray對象
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hasCode, true)) {
owner.splitFlags[splitIndex] |= ApplicationInfo.FLAG_HAS_CODE;
}
final int innerDepth = parser.getDepth();
int type;
// 開始解析manifest的XML
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
// 獲取標簽名
String tagName = parser.getName();
// 如果標簽是<activity>
if (tagName.equals("activity")) {
// 解析<activity>標簽
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
// 解析失敗
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// 將解析出來的activity添加到activities中
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
// 解析<receiver>標簽
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
// 解析失敗
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// 將解析出來的activity添加到activities中
owner.receivers.add(a);
} else if (tagName.equals("service")) {
// 解析<service>標簽
Service s = parseService(owner, res, parser, flags, outError);
// 解析失敗
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// 將解析出來的service添加到services中
owner.services.add(s);
} else if (tagName.equals("provider")) {
// 解析<provider>標簽
Provider p = parseProvider(owner, res, parser, flags, outError);
// 解析事變
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// 將解析出來的providers添加到providers中
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
// 解析<activity-alias>標簽
Activity a = parseActivityAlias(owner, res, parser, flags, outError);
// 解析失敗
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// 將解析出來的activity添加到activities中
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
// 解析<meta-data>標簽
if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("uses-library")) {
// 如果標簽是<uses-library>
// 重新獲取TypedArray對象sa
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
// 獲取庫名字
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
// 是否設置了required=true
boolean req = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
true);
// 回收 sa
sa.recycle();
if (lname != null) {
// 獲取字符串常量池的值
lname = lname.intern();
if (req) {
// 如果設置了required=true
// Upgrade to treat as stronger constraint
則將這庫添加進usesLibraries去
owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
owner.usesOptionalLibraries = ArrayUtils.remove(
owner.usesOptionalLibraries, lname);
} else {
// 如果沒有設置required=true并且則將這庫不在usesLibraries中,則將這個庫添加到usesOptionalLibraries中
// Ignore if someone already defined as required
if (!ArrayUtils.contains(owner.usesLibraries, lname)) {
owner.usesOptionalLibraries = ArrayUtils.add(
owner.usesOptionalLibraries, lname);
}
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-package")) {
// Dependencies for app installers; we don't currently try to
// enforce this.
XmlUtils.skipCurrentTag(parser);
} else {
// RIGID_PARSER為常量一直未false
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <application>: " + tagName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
return true;
}
有注釋,先來翻譯一下注釋:
解析"拆分APK"的XML樹中的< application >標簽節點。
注意:由于對"拆分APK"限制比較多,所以像"base APK"的很多功能在"拆分APK"中已經省略了。
這個方法主要就是解析幾個對應的標簽。
十九、PackageParse#parseActivity(Package, Resources,XmlPullParser, AttributeSet, int, String[],boolean, boolean)方法解析
代碼在PackageParser.java 3026行
private Activity parseActivity(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
// 獲取資源數組
TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity);
// 初始化解析Activity參數
if (mParseActivityArgs == null) {
mParseActivityArgs = new ParseComponentArgs(owner, outError,
R.styleable.AndroidManifestActivity_name,
R.styleable.AndroidManifestActivity_label,
R.styleable.AndroidManifestActivity_icon,
R.styleable.AndroidManifestActivity_logo,
R.styleable.AndroidManifestActivity_banner,
mSeparateProcesses,
R.styleable.AndroidManifestActivity_process,
R.styleable.AndroidManifestActivity_description,
R.styleable.AndroidManifestActivity_enabled);
}
// 判斷標簽類型是 receiver還是activity
mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
// 初始化mParseActivityArgs的兩個屬性
mParseActivityArgs.sa = sa;
mParseActivityArgs.flags = flags;
// 創建一個Activity(這里的Activity不是我們平時說的Activity,而是PackageParse的靜態內部類Activity)
Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
if (outError[0] != null) {
// 沒有出現問題,則回收
sa.recycle();
return null;
}
// 是否在AndroidManifest中設置了"android:exported"屬性
boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
if (setExported) {
// 如果設置了,則進行配置
a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false);
}
// 設置AndroidManifest里面對應的theme的值
a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
// 設置AndroidManifest里面對應的uiOptions的值
a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
a.info.applicationInfo.uiOptions);
// 獲取AndroidManifest里面"android:parentActivityName=String"的值
String parentName = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_parentActivityName,
Configuration.NATIVE_CONFIG_VERSION);
// 如果設置了android:parentActivityName
if (parentName != null) {
// 構建parent的類名
String parentClassName = buildClassName(a.info.packageName, parentName, outError);
if (outError[0] == null) {
a.info.parentActivityName = parentClassName;
} else {
Log.e(TAG, "Activity " + a.info.name + " specified invalid parentActivityName " +
parentName);
outError[0] = null;
}
}
// 獲取權限permission
String str;
str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
if (str == null) {
a.info.permission = owner.applicationInfo.permission;
} else {
a.info.permission = str.length() > 0 ? str.toString().intern() : null;
}
// 獲取是否有在這個Activity中配置了taskAffinity(吸附值)這個屬性
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
// 設置吸附值
a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
owner.applicationInfo.taskAffinity, str, outError);
// 是否在AndroidManfest里面這個activity是否配置了"android:multiprocess=boolean" 屬性,如果設置了true,則該activity支持多進程
a.info.flags = 0;
if (sa.getBoolean(
R.styleable.AndroidManifestActivity_multiprocess, false)) {
a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
}
// 是否在AndroidManfest里面這個activity是否配置了"android:finishOnTaskLaunch=boolean" 屬性
// 該標識用來標識每當用戶再次啟動其任務(在主屏幕上選擇任務)時,是否應該關閉現有Activity實例。
// "true"表示應該關閉,"false"表示不關閉。默認值為"false"。(sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
}
//是否在AndroidManfest里面這個activity是否配置了"android:clearTaskOnLaunch=boolean" 屬性
// 這個標識用來指明當前應用從主屏幕重新啟動時是否都從中移除根Activity之外的所有Activity。
//"true"表示始終將任務清楚到只剩根Activity,"false"表示不清楚,默認值為false
if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
}
// 是否在AndroidManfest里面這個activity是否配置了"android:noHistory=boolean" 屬性
// 該標示用來指定 當用戶離開Activity,并且其再屏幕上不在可見時,是否應從Activity 堆棧中將其移除并完成finish()操作
// "true"表示應該將其finish,"false"表示不應該將其finish。默認值為false。
if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
}
// 是否在AndroidManfest里面這個activity是否配置了"android:alwaysRetainTaskState=boolean" 屬性
// 這個標示用來指示系統是否始終保持Activity所在的任務的狀態。
// "true" 表示支持,"false"表示允許系統在特定的情況下將任務重置到初始狀態。默認值是false。
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
}
// 是否在AndroidManfest里面這個activity是否配置了"android:stateNotNeeded=boolean" 屬性
// 該標識用來指明能否在不保存Activity的情況下將其終止并成功重啟
// "true" 表示可在不考慮其之前狀態情況下重新啟動,"false"表示需要之前的狀態,默認值"false"。
if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
}
// 是否在AndroidManifest里面設置"android:excludeFromRecents=boolean"
// 該標識用來表示是否應該將Activity啟動的任務是否排除在最近使用的應用列表之外。
// "true"表示將任務排除在列表之外,"false"表示包含在內。默認值為"false"
if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
}
// 是否在AndroidManifest里面設置"android:allowTaskReparenting=boolean"
// 表明這個應用在reset task時,是不是關聯對應的taskAffinity值。true,關聯;false,不關聯
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
(owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
}
// 是否在AndroidManifest里面設置"android:finishOnCloseSystemDialogs=boolean"
// 表示 當"關閉系統窗口"請求出現時,是否銷毀Activity,true銷毀,false不銷毀
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
}
// 是否在AndroidManifest里面設置"android:showOnLockScreen=boolean"
// 指定該Activity是否顯示在解鎖界面,true顯示,false 不顯示
// 是否在AndroidManifest里面設置"android:showOnLockScreen=boolean"
// 指定該Activity是否可以顯示給所有用戶,true可以,false 不可以
if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
|| sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
}
// 是否在AndroidManifest里面設置"android:immersive=boolean" 是否設置沉浸式顯示,true是,false不是
if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
// 是否在AndroidManifest里面設置"android:primaryUserOnly=boolean"
// 是否設置視為系統組件,true是,false不是
if (sa.getBoolean(R.styleable.AndroidManifestActivity_primaryUserOnly, false)) {
a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
}
// 如果不是receiver標簽,同時是否設置了硬件加速
if (!receiver) {
if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
hardwareAccelerated)) {
a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
}
// 設置對應的啟動模式,對應launchMode
a.info.launchMode = sa.getInt(
R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
// 設置對應的啟動模式,對應documentLaunchMode,主要指概覽屏幕(recent app)
a.info.documentLaunchMode = sa.getInt(
R.styleable.AndroidManifestActivity_documentLaunchMode,
ActivityInfo.DOCUMENT_LAUNCH_NONE);
// 概覽屏幕中此Activity的根位置的任務數上線
a.info.maxRecents = sa.getInt(
R.styleable.AndroidManifestActivity_maxRecents,
ActivityManager.getDefaultAppRecentsLimitStatic());
// 是否設置了android:configChanges
a.info.configChanges = sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0);
a.info.softInputMode = sa.getInt(
R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
a.info.persistableMode = sa.getInteger(
R.styleable.AndroidManifestActivity_persistableMode,
ActivityInfo.PERSIST_ROOT_ONLY);
// 是否設置了android:allowEmbedded=boolean,true表示該Activity可以作為另一個Activity的的嵌入式子項啟動。
//主要適用于可穿戴設備。
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
}
// 是否設置了android:autoRemoveFromRecents=boolean
// 表示是否Activity一直保留在概覽屏幕中,直到任務中的最后一個Activity finish為止。
// true,則自動從概覽屏幕中移除任務
if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, false)) {
a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
}
// 是否設置了android:relinquishTaskIdentity=boolean
// 是否將其任務的標識符交給任務棧中在其之上的Activity。如果任務根Activity的該屬性設置"true",
// 則任務會用其內的下一個Activity的Intent替換基本的Intent,直到某個Activity將其屬性設置為"false"為止。
if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, false)) {
a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
}
// 是否設置了android:resumeWhilePausing=boolean
// 表示前一個Activity執行onPause的時候,當前是否Activity繼續顯示
if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
}
// 是否設置了android:resizeableActivity=boolean
// 這個標識表示 是否支持分屏
a.info.resizeable = sa.getBoolean(
R.styleable.AndroidManifestActivity_resizeableActivity, false);
if (a.info.resizeable) {
// Fixed screen orientation isn't supported with resizeable activities.
a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
} else {
a.info.screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
// 獲取對應的鎖屏模式
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
} else {
// 如果是receiver標簽
// 設置啟動模式
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
Slog.w(TAG, "Activity exported request ignored due to singleUser: "
+ a.className + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
a.info.exported = false;
setExported = true;
}
}
}
sa.recycle();
// 判斷是否是重量級的APP。如果是重量級的APP,則不應該有receiver
if (receiver && (owner.applicationInfo.privateFlags
&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
// A heavy-weight application can not have receives in its main process
// We can do direct compare because we intern all strings.
if (a.info.processName == owner.packageName) {
outError[0] = "Heavy-weight applications can not have receivers in main process";
}
}
if (outError[0] != null) {
return null;
}
int outerDepth = parser.getDepth();
int type;
// 開始解析<activity></activity>的內部標簽
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
//如果解析到<ntent-filter>標簽
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
// 解析<ntent-filter>標簽
if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
// 添加進去
a.intents.add(intent);
}
} else if (!receiver && parser.getName().equals("preferred")) {
// 如果是<activity>,且遇到<preferred> 標簽
ActivityIntentInfo intent = new ActivityIntentInfo(a);
// 解析<preferred> 標簽
if (!parseIntent(res, parser, attrs, false, false, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
if (owner.preferredActivityFilters == null) {
owner.preferredActivityFilters = new ArrayList<ActivityIntentInfo>();
}
// 添加
owner.preferredActivityFilters.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
//遇到<meta-data> 標簽
// 解析<meta-data> 標簽
if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
outError)) == null) {
return null;
}
} else {
// 如果遇到其他的 莫名其妙的標簽
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
if (receiver) {
Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else {
if (receiver) {
outError[0] = "Bad element under <receiver>: " + parser.getName();
} else {
outError[0] = "Bad element under <activity>: " + parser.getName();
}
return null;
}
}
}
if (!setExported) {
a.info.exported = a.intents.size() > 0;
}
return a;
}
這個方法主要是解析AndroidManifest.xml里面的<activity>標簽的內容,并將其映射到PackageParse.Activity對象
掃描Package的第一部分工作,難度不大,但極其的繁瑣,跟著流程走一邊真是想死的心都有了。不過正如Torvalds大神所說的,”RTFSC, read the fucking source code”,耐著性子多看看,是提高的基礎條件。
上圖畫出了PackageParser解析Apk文件,得到的主要的數據結構,實際的內容遠多于這些,我們僅保留了四大組件和權限相關的內容。
上面這些類,全部是定義于PackageParser中的內部類,這些內部類主要的作用就是保存AndroidManifest.xml解析出的對應信息。
以PackageParser.Activity為例,注意到該類持有ActivityInfo類,繼承自Component< ActivityIntentInfo>。其中,ActivityInfo用于保存Activity的信息;Component類是一個模板,對應元素類型是ActivityIntentInfo,頂層基類為IntentFilter。四大組件中的其它成員,也有類似的繼承結構。
這種設計的原因是:Package除了保存信息外,還需要支持Intent匹配查詢。例如,當收到某個Intent后,由于ActivityIntentInfo繼承自IntentFilter,因此它能判斷自己是否滿足Intent的要求。如果滿足,則返回對應的ActivityInfo。
二十、總結
最后,我們結合上圖回憶一下整個掃描過程:
- PackageParser首先解析出了ApkLite,得到每個Apk文件的簡化信息(對于具有多個Apk文件的Package來說,將得到多個ApkLite);
- 利用所有的ApkLite及XML中的其它信息,解析出PackageLite;
- 利用PackageLite中的信息及XML中的其它信息,解析出Package信息;Package中就基本上涵蓋了AndroidManifest.xml中涉及的所有信息。
注意在上述的解析過程中,PackageParser利用AssetManager存儲了Package中資源文件的地址。
上一篇文章 APK安裝流程詳解9——PackageParser解析APK(上)
下一篇文章 APK安裝流程詳解11——普通應用安裝簡介