編輯:關於Android編程
我們分析一些源碼的實現.首先從PMS服務開始.
PMS服務即PackageManagerService,主要用來進行APK的管理任務.但是今天,我們並不直接分析PMS的源碼,而是從一個工具類PackageParse說起.
首先來認識PackageParser類,它主要用來解析手機上的apk文件(支持Single APK和Multiple APK),主要包含兩個過程
APK->Package:解析APK文件為Package對象的過程 Package->PackageInfo:由Package對象生成PackageInfo的過程介於不少童鞋不了解Single APK和Multiple APK,在這裡做個簡單解釋:
Single APK是我們通常所開發的APK,即一個應用只有一個apk文件.而Google Play還允許你為一個應用中發布不同的apk文件,這些apk文件適用於不同設備.舉例說明,假設你現在開發了一款APP叫做Monkey,但是目前該APP由於體積太大或者其他因素導致不能同時適用於手機和平板,此時你就可將原先的Monkey.apk拆分為了Monkey-Phone.apk和Monkey-Tablet,分別用於運行在Android手機和Android平板,只要保存兩者擁有相同的包名,並用相同key進行簽名就可以在發布Monkey應用的時候,一同發布Monkey-Phone.apk和Moneky-Tablet.apk,那麼這種一個應用擁有多個APK文件的程序就稱之為Multiple APK.
更多信息查看官網:multiple-apks
該過程目的是通過解析磁盤上的APK文件來生成與之相關的Package對象.而Pakcage對象是APK經過完整解析之後的結果.
該過程主要涉及到一系列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);
}
}
該方法接受兩個參數packageFile和flags.並根據packageFile是否是文件目錄來確定具體的解析流程.通常我們都是Single APK,因此我們重點關注Single APK的解析,不難發現其具體解析過程給委托給parseMonolithicPackage(packageFile, flags)
方法:
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);
}
}
在該方法中首先通過mOnlyCoreApps屬性判斷當前系統是不是只解析核心APK,默認是全部解析.至於什麼是核心APK後面再說.現在我們繼續關注其解析過程.
這裡其解析操作繼續由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;
}
...
}
而真正的解析又是通過該方法的同名函數: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")) {
//....省略多行代碼....
}
//....省略多行代碼....
}
//....省略多汗代碼....
}
不難發現這裡通過很多parseXXX()方法解析相應的數據,比如:parseBaseApplication(),parseKeySets(),parsePermissionGroup(),parseUsesPermission()
等等.
下面,我們重點關注Application標簽的解析,即:parseBaseApplication()
方法:
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
//....省略對Application元素屬性解析多行代碼....
//解析Application下的子元素結點,如activity,receiver,service等
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (tagName.equals("activity")) {
//....省略多行代碼,主要關注parseActivity()....
} else if (tagName.equals("receiver")) {
//....省略多行代碼,主要關注parseActivity()....
}else if (tagName.equals("service")) {
//....省略多行代碼,主要關注parseService()....
}else if (tagName.equals("provider")) {
//....省略多行代碼,主要關注parseProvider()....
}else if (tagName.equals("activity-alias")) {
//....省略多行代碼,主要關注parseActivityAlias()...
}else if (parser.getName().equals("meta-data")) {
//....省略多行代碼,重點關注parseMetaData()....
}else if (tagName.equals("library")) {
//....省略多行代碼....
}else if (tagName.equals("uses-library")) {
//....省略多行代碼....
}else if (tagName.equals("uses-package")) {
//....省略多行代碼....
}else{
//....省略多行代碼....
}
}
return true;
}
在解析Application下子元素結點時,同樣也是通過很多parseXXX()方法來完成的.比如在解析activity結點時是通過parseActivity()
來完成的,其余自行查閱代碼.
另外你可能已經注意到對receiver的解析也是通過parseActivity()
實現的.
到此為止,整個為止,解析的整個流程完成,並返回一個Package對象.
附:PackageParser中所有相關解析方法
該過程的目的是從Package中提取相關屬性,並封裝成PackageInfo類型的對象.
該過程主要涉及到一系列generateXXXInfo()
格式的方法,起始方法是generatePackageInfo()
,那麼我們就從該方法開始分析其流程:
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
ArraySet grantedPermissions, PackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, UserHandle.getCallingUserId());
}
不難看出這裡由調用了其同名方法generatePackageInfo(PackageParser.Package p,
來進行繼續解析工作:
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
ArraySet
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
ArraySet 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) {
//....省略多行代碼,關注generateActivityInfo()....
}
if ((flags&PackageManager.GET_RECEIVERS) != 0) {
//....省略多行代碼,關注generateActivityInfo()....
}
if ((flags&PackageManager.GET_SERVICES) != 0) {
//....省略多行代碼,關注generateServiceInfo()....
}
if ((flags&PackageManager.GET_PROVIDERS) != 0) {
//....省略多行代碼,關注generateProviderInfo()....
}
if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
//....省略多行代碼,關注generateInstrumentationInfo()....
}
if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
//....省略多行代碼,generatePermissionInfo....
}
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
//....省略多行代碼....
}
return pi;
}
上面的過程主要從Package對象取出一系列的屬性用來初始化PackageInfo對象,該過程不再涉及磁盤文件的解析操作.
和解析過程相對,該過程借助了很多generateXXXInfo()
方法來實現.在解析過程中對於Application元素的解析提供了parseApplication()
,而在該過程中也提供了generateApplicationInfo()
來實現Application的取值操作
附:PackageParser中所有相關的generate方法
到現在為止,我們已經了解Package的生成和PackageInfo生成,不難發現Package的生成是以磁盤APK文件作為輸入,而PackageInfo是以Package對象作為輸入.得益於Google工程師良好的設計,PackageParse具有非常好的對稱性,非常容易理解.在這裡,我只是簡單的介紹了該類,對於具體的操作並沒有深入的說明,其原因在於,其核心就是通過使用Pull Parser對xml文件進行解析的操作.
附:PackageParser所有內部類:
細心的同學已經發現在上面所示的內部類中也存在Activity,Service等類,要注意這些並不是我們平常使用的Activity組件.PackageParser中的內部類,如Activity,Service,Provider,Permission,皆對應於AndroidManifest.xml文件中的某個標簽,用於存儲解析出來相關的信息.<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPtXiwO/O0sPHzazR+dPDwODNvMC0vPK1pbXEw+jK9sbavOS1xLnYz7U6PGJyIC8+DQo8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160711/20160711091100958.jpg" title="\" />
接下來,我們來介紹與上述過程相關的幾個實體類,以便你有一個宏觀的認識,從而為理解後面的PMS打下基礎.
對於這幾個實體類,我們值做簡單的說明,其具體的點還是需要我們自己進行深究.
PackageParser的靜態內部類,代表磁盤上APK文件完整解析後的對象,相當於在內存中Package的對象是對磁盤APK的描述.這裡我們只需要關注其屬性即可,大部分屬性對你而來都是很熟悉的:
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();
//權限
public final ArrayList permissions = new ArrayList(0);
public final ArrayList permissionGroups = new ArrayList(0);
//四大組件Activity,Receiver,Service,Provider
public final ArrayList activities = new ArrayList(0);
public final ArrayList receivers = new ArrayList(0);
public final ArrayList providers = new ArrayList(0);
public final ArrayList services = new ArrayList(0);
public final ArrayList instrumentation = new ArrayList(0);
public final ArrayList requestedPermissions = new ArrayList();
public final ArrayList requestedPermissionsRequired = new ArrayList();
public ArrayList protectedBroadcasts;
public ArrayList libraryNames = null;
public ArrayList usesLibraries = null;
public ArrayList usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
public ArrayList preferredActivityFilters = null;
public ArrayList mOriginalPackages = null;
public String mRealPackage = null;
public ArrayList 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 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 configPreferences = null;
// Applications requested features
public ArrayList reqFeatures = null;
// Applications requested feature groups
public ArrayList 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 mSigningKeys;
public ArraySet mUpgradeKeySets;
public ArrayMap> mKeySetMapping;
public String cpuAbiOverride;
}
該類代表包的整體描述信息,即AndroidManifest.xml中的信息.如果說Package在內存中代表完整的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很容易發現期間的關系,接下來順便介紹PackageInfo中涉及到的實體類:
和
元素的信息
ServiceInfo
該實體類代表AndroidManiest.xml中的
元素中的信息
ProviderInfo
該實體類代表AndroidManiest.xml中的
元素的信息
InstrumentationInfo
該實體類代表AndroidManiest.xml中的
元素的信息
PermissionInfo
該實體類代表AndroidManiest.xml中的
元素的信息
ConfigurationInfo
關於程序要求的硬件信息,該實體類代表AndroidManiest.xml中
和
元素的信息.
FeatureInfo
該實體類代表AndroidManiest.xml中的
元素的信息
FeatureGroupInfo
該實體類代表AndroidManiest.xml中的
元素的信息
ManifestDigest
代表一個包文件的摘要信息
這裡我們用一張類圖來描述其類間的關系:
到現在PackageParser的基本解釋已經完成,之所以在分析PMS之前先來談PackageParser的原因在於,該工具類可以脫離上下文,單獨進行理解,而無關你目前的狀態,這也就避免我們面對一大堆源碼,在閱讀過程找不到側重點的問題.接下來,是對PackageManager的分析.
本文在《7種形式的Android Dialog使用實例》在這篇文章的基礎進行學習,具體內容如下1.概述 android原生控件向來以丑著稱(新推出的Material D
照例先上效果圖通過該例子,你能學到什麼: 對Paint 深入理解,畫繪制餅圖,矩形,文字等 &nbs
布局文件復制代碼 代碼如下:<RelativeLayout xmlns:android=http://schemas.android.com/apk/res/and
先放兩張效果圖 一、准備由於AndroidStudio不具備開發插件的功能,需要安裝IDEA 翻譯使用的是有道的翻譯接口,需要申請,接口申請的網址點這裡 json解析使用