在前面兩篇自己動手編譯最新Android源碼及自己動手調(diào)試Android源碼中,我們掌握了Android源碼的編譯以及調(diào)試,現(xiàn)在呢,我們在這基礎(chǔ)上分析一些源碼的實現(xiàn).首先從PMS服務(wù)開始.
PMS服務(wù)即PackageManagerService,主要用來進(jìn)行APK的管理任務(wù).但是今天,我們并不直接分析PMS的源碼,而是從一個工具類PackageParse說起.
首先來認(rèn)識PackageParser類,它主要用來解析手機上的apk文件(支持Single APK和Multiple APK),主要包含兩個過程
- APK->Package:解析APK文件為Package對象的過程
- Package->PackageInfo:由Package對象生成PackageInfo的過程
介于不少童鞋不了解Single APK和Multiple APK,在這里做個簡單解釋:
Single APK是我們通常所開發(fā)的APK,即一個應(yīng)用只有一個apk文件.而Google Play還允許你為一個應(yīng)用中發(fā)布不同的apk文件,這些apk文件適用于不同設(shè)備.舉例說明,假設(shè)你現(xiàn)在開發(fā)了一款A(yù)PP叫做Monkey,但是目前該APP由于體積太大或者其他因素導(dǎo)致不能同時適用于手機和平板,此時你就可將原先的Monkey.apk拆分為了Monkey-Phone.apk和Monkey-Tablet,分別用于運行在Android手機和Android平板,只要保存兩者擁有相同的包名,并用相同key進(jìn)行簽名就可以在發(fā)布Monkey應(yīng)用的時候,一同發(fā)布Monkey-Phone.apk和Moneky-Tablet.apk,那么這種一個應(yīng)用擁有多個APK文件的程序就稱之為Multiple APK.
更多信息查看官網(wǎng):multiple-apks
解析APK文件為Package對象
該過程目的是通過解析磁盤上的APK文件來生成與之相關(guān)的Package對象.而Pakcage對象是APK經(jīng)過完整解析之后的結(jié)果.
該過程主要涉及到一系列parseXXX()
格式的方法,起始方法是public Package parsePackage(File packageFile, int flags)
,那么我們就從該方法開始分析其流程:
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
//多個apk文件的目錄
return parseClusterPackage(packageFile, flags);
} else {
//單一APK文件
return parseMonolithicPackage(packageFile, flags);
}
}
該方法接受兩個參數(shù)packageFile和flags.并根據(jù)packageFile是否是文件目錄來確定具體的解析流程.通常我們都是Single APK,因此我們重點關(guān)注Single APK的解析,不難發(fā)現(xiàn)其具體解析過程給委托給parseMonolithicPackage(packageFile, flags)
方法:
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//如果是核心應(yīng)用則以更輕量級的方式進(jìn)行解析后,判斷是否是核心應(yīng)用,非核心應(yīng)用不執(zhí)行解析過程
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 {
//調(diào)用parseBaseAPK()繼續(xù)解析操作
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
在該方法中首先通過mOnlyCoreApps屬性判斷當(dāng)前系統(tǒng)是不是只解析核心APK,默認(rèn)是全部解析.至于什么是核心APK后面再說.現(xiàn)在我們繼續(xù)關(guān)注其解析過程.
這里其解析操作繼續(xù)由parseBaseApk(apkFile, assets, flags)
完成:
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
...
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);
//為AndroidManifest.xml生成xml文件解析器
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(res, parser, flags, outError);
...
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
return pkg;
}
...
}
而真正的解析又是通過該方法的同名函數(shù):parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError)
完成的,為了突出重點,我對其方法進(jìn)行了簡化:
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
//....省略多行代碼....
//循環(huán)解析AndroidManifest.xml中的元素
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (tagName.equals("application")) {
//....省略多行代碼,重點關(guān)注其中調(diào)用的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")) {
//....省略多行代碼....
}
//....省略多行代碼....
}
//....省略多汗代碼....
}
不難發(fā)現(xiàn)這里通過很多parseXXX()方法解析相應(yīng)的數(shù)據(jù),比如:parseBaseApplication(),parseKeySets(),parsePermissionGroup(),parseUsesPermission()
等等.
下面,我們重點關(guān)注Application標(biāo)簽的解析,即:parseBaseApplication()
方法:
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
//....省略對Application元素屬性解析多行代碼....
//解析Application下的子元素結(jié)點,如activity,receiver,service等
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (tagName.equals("activity")) {
//....省略多行代碼,主要關(guān)注parseActivity()....
} else if (tagName.equals("receiver")) {
//....省略多行代碼,主要關(guān)注parseActivity()....
}else if (tagName.equals("service")) {
//....省略多行代碼,主要關(guān)注parseService()....
}else if (tagName.equals("provider")) {
//....省略多行代碼,主要關(guān)注parseProvider()....
}else if (tagName.equals("activity-alias")) {
//....省略多行代碼,主要關(guān)注parseActivityAlias()...
}else if (parser.getName().equals("meta-data")) {
//....省略多行代碼,重點關(guān)注parseMetaData()....
}else if (tagName.equals("library")) {
//....省略多行代碼....
}else if (tagName.equals("uses-library")) {
//....省略多行代碼....
}else if (tagName.equals("uses-package")) {
//....省略多行代碼....
}else{
//....省略多行代碼....
}
}
return true;
}
在解析Application下子元素結(jié)點時,同樣也是通過很多parseXXX()方法來完成的.比如在解析activity結(jié)點時是通過parseActivity()
來完成的,其余自行查閱代碼.
另外你可能已經(jīng)注意到對receiver的解析也是通過parseActivity()
實現(xiàn)的.
到此為止,整個為止,解析的整個流程完成,并返回一個Package對象.
附:PackageParser中所有相關(guān)解析方法
由Package對象生成PackageInfo
該過程的目的是從Package中提取相關(guān)屬性,并封裝成PackageInfo類型的對象.
該過程主要涉及到一系列generateXXXInfo()
格式的方法,起始方法是generatePackageInfo()
,那么我們就從該方法開始分析其流程:
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
ArraySet<String> grantedPermissions, PackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, UserHandle.getCallingUserId());
}
不難看出這里由調(diào)用了其同名方法generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, ArraySet<String> grantedPermissions, PackageUserState state, int userId)
來進(jìn)行繼續(xù)解析工作:
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
ArraySet<String> grantedPermissions, PackageUserState state, int userId)
if (!checkUseInstalledOrHidden(flags, state)) {
return null;
}
//從Package對象p中取出一系列的屬性值用來初始化pi
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.splitNames = p.splitNames;
pi.versionCode = p.mVersionCode;
pi.baseRevisionCode = p.baseRevisionCode;
pi.splitRevisionCodes = p.splitRevisionCodes;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
pi.coreApp = p.coreApp;
if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
|| (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
pi.requiredForAllUsers = p.mRequiredForAllUsers;
}
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
pi.overlayTarget = p.mOverlayTarget;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
pi.gids = gids;
}
if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
//....省略多行代碼....
}
if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
//....省略多行代碼,關(guān)注generateActivityInfo()....
}
if ((flags&PackageManager.GET_RECEIVERS) != 0) {
//....省略多行代碼,關(guān)注generateActivityInfo()....
}
if ((flags&PackageManager.GET_SERVICES) != 0) {
//....省略多行代碼,關(guān)注generateServiceInfo()....
}
if ((flags&PackageManager.GET_PROVIDERS) != 0) {
//....省略多行代碼,關(guān)注generateProviderInfo()....
}
if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
//....省略多行代碼,關(guān)注generateInstrumentationInfo()....
}
if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
//....省略多行代碼,generatePermissionInfo....
}
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
//....省略多行代碼....
}
return pi;
}
上面的過程主要從Package對象取出一系列的屬性用來初始化PackageInfo對象,該過程不再涉及磁盤文件的解析操作.
和解析過程相對,該過程借助了很多generateXXXInfo()
方法來實現(xiàn).在解析過程中對于Application元素的解析提供了parseApplication()
,而在該過程中也提供了generateApplicationInfo()
來實現(xiàn)Application的取值操作
附:PackageParser中所有相關(guān)的generate方法
中途小結(jié)
到現(xiàn)在為止,我們已經(jīng)了解Package的生成和PackageInfo生成,不難發(fā)現(xiàn)Package的生成是以磁盤APK文件作為輸入,而PackageInfo是以Package對象作為輸入.得益于Google工程師良好的設(shè)計,PackageParse具有非常好的對稱性,非常容易理解.在這里,我只是簡單的介紹了該類,對于具體的操作并沒有深入的說明,其原因在于,其核心就是通過使用Pull Parser對xml文件進(jìn)行解析的操作.
附:PackageParser所有內(nèi)部類:
(細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)在上面所示的內(nèi)部類中也存在Activity,Service等類,要注意這些并不是我們平常使用的Activity組件.)
相關(guān)實體類
接下來,我們來介紹與上述過程相關(guān)的幾個實體類,以便你有一個宏觀的認(rèn)識,從而為理解后面的PMS打下基礎(chǔ).
對于這幾個實體類,我們值做簡單的說明,其具體的點還是需要我們自己進(jìn)行深究.
Package
PackageParser的靜態(tài)內(nèi)部類,代表磁盤上APK文件完整解析后的對象,相當(dāng)于在內(nèi)存中Package的對象是對磁盤APK的描述.這里我們只需要關(guān)注其屬性即可,大部分屬性對你而來都是很熟悉的:
public final static class Package {
public String packageName;
/** Names of any split APKs, ordered by parsed splitName */
public String[] splitNames;
//apk文件在磁盤的路徑.可能是一個apk的路徑,也可能是包含多個apk文件的目錄
public String codePath;
/** Path of base APK */
public String baseCodePath;
/** Paths of any split APKs, ordered by parsed splitName */
public String[] splitCodePaths;
/** Revision code of base APK */
public int baseRevisionCode;
/** Revision codes of any split APKs, ordered by parsed splitName */
public int[] splitRevisionCodes;
/** Flags of any split APKs; ordered by parsed splitName */
public int[] splitFlags;
public boolean baseHardwareAccelerated;
public final ApplicationInfo applicationInfo = new ApplicationInfo();
//權(quán)限
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
//四大組件Activity,Receiver,Service,Provider
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
public ArrayList<String> protectedBroadcasts;
public ArrayList<String> libraryNames = null;
public ArrayList<String> usesLibraries = null;
public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
public ArrayList<String> mOriginalPackages = null;
public String mRealPackage = null;
public ArrayList<String> mAdoptPermissions = null;
// We store the application meta-data independently to avoid multiple unwanted references
public Bundle mAppMetaData = null;
// The version code declared for this package.
public int mVersionCode;
// The version name declared for this package.
public String mVersionName;
// The shared user id that this package wants to use.
public String mSharedUserId;
// The shared user label that this package wants to use.
public int mSharedUserLabel;
// Signatures that were read from the package.
public Signature[] mSignatures;
public Certificate[][] mCertificates;
// For use by package manager service for quick lookup of
// preferred up order.
public int mPreferredOrder = 0;
// For use by package manager to keep track of where it needs to do dexopt.
public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);
// For use by package manager to keep track of when a package was last used.
public long mLastPackageUsageTimeInMills;
// // User set enabled state.
// public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
//
// // Whether the package has been stopped.
// public boolean mSetStopped = false;
// Additional data supplied by callers.
public Object mExtras;
// Whether an operation is currently pending on this package
public boolean mOperationPending;
// Applications hardware preferences
public ArrayList<ConfigurationInfo> configPreferences = null;
// Applications requested features
public ArrayList<FeatureInfo> reqFeatures = null;
// Applications requested feature groups
public ArrayList<FeatureGroupInfo> featureGroups = null;
public int installLocation;
public boolean coreApp;
/* An app that's required for all users and cannot be uninstalled for a user */
public boolean mRequiredForAllUsers;
/* The restricted account authenticator type that is used by this application */
public String mRestrictedAccountType;
/* The required account type without which this application will not function */
public String mRequiredAccountType;
/**
* 代表一個包文件的摘要,用于確定兩個package是否不一致
*/
public ManifestDigest manifestDigest;
public String mOverlayTarget;
public int mOverlayPriority;
public boolean mTrustedOverlay;
/**
* Data used to feed the KeySetManagerService
*/
public ArraySet<PublicKey> mSigningKeys;
public ArraySet<String> mUpgradeKeySets;
public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
public String cpuAbiOverride;
}
PackageInfo
該類代表包的整體描述信息,即AndroidManifest.xml中的信息.如果說Package在內(nèi)存中代表完整的APK描述,那么PackageInfo則是其子集,來簡單的看一下其代碼:
public class PackageInfo implements Parcelable {
public String packageName;
public String[] splitNames;
public int versionCode;
public String versionName;
public int baseRevisionCode;
public int[] splitRevisionCodes;
public String sharedUserId;
public int sharedUserLabel;
public ApplicationInfo applicationInfo;
public long firstInstallTime;
public long lastUpdateTime;
public int[] gids;
public ActivityInfo[] activities;
public ActivityInfo[] receivers;
public ServiceInfo[] services;
public ProviderInfo[] providers;
public InstrumentationInfo[] instrumentation;
public PermissionInfo[] permissions;
public String[] requestedPermissions;
public int[] requestedPermissionsFlags;
public Signature[] signatures;
public ConfigurationInfo[] configPreferences;
public FeatureInfo[] reqFeatures;
public FeatureGroupInfo[] featureGroups;
}
對比Package和PackageInfo很容易發(fā)現(xiàn)期間的關(guān)系,接下來順便介紹PackageInfo中涉及到的實體類:
類名 | 描述 |
---|---|
ActivityInfo | 該實體類代表AndroidManiest.xml中的<activity> 和<recevier> 元素的信息 |
ServiceInfo | 該實體類代表AndroidManiest.xml中的<service> 元素中的信息 |
ProviderInfo | 該實體類代表AndroidManiest.xml中的<provider> 元素的信息 |
InstrumentationInfo | 該實體類代表AndroidManiest.xml中的<instrumentation> 元素的信息 |
PermissionInfo | 該實體類代表AndroidManiest.xml中的<permission> 元素的信息 |
ConfigurationInfo | 關(guān)于程序要求的硬件信息,該實體類代表AndroidManiest.xml中<uses-configuration> 和<uses-feature> 元素的信息. |
FeatureInfo | 該實體類代表AndroidManiest.xml中的<uses-feature> 元素的信息 |
FeatureGroupInfo | 該實體類代表AndroidManiest.xml中的<feature-group> 元素的信息 |
ManifestDigest | 代表一個包文件的摘要信息 |
這里我們用一張類圖來描述其類間的關(guān)系:
總結(jié)
到現(xiàn)在PackageParser的基本解釋已經(jīng)完成,之所以在分析PMS之前先來談PackageParser的原因在于,該工具類可以脫離上下文,單獨進(jìn)行理解,而無關(guān)你目前的狀態(tài),這也就避免我們面對一大堆源碼,在閱讀過程找不到側(cè)重點的問題.接下來,是對PackageManager的分析.