Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入分析PMS服務(一)

深入分析PMS服務(一)

編輯:關於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對象

該過程目的是通過解析磁盤上的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

該過程的目的是從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 grantedPermissions, PackageUserState state, int userId)
來進行繼續解析工作:

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打下基礎.
對於這幾個實體類,我們值做簡單的說明,其具體的點還是需要我們自己進行深究.

Package

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;

    }

PackageInfo

該類代表包的整體描述信息,即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中涉及到的實體類:

類名 描述 ActivityInfo 該實體類代表AndroidManiest.xml中的元素的信息 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的分析.

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved