Apk安裝的源碼分析(三)

  • 概述

    前面兩篇博文分析了apk安裝的流程,我們換個角度,從apk啟動來看安裝的信息是如何使用的,這個過程也可能會發(fā)現(xiàn)安裝流程中忽視掉的邏輯,從而使頭尾相連,把整個安裝原理搞明白。

    整個思路就是根據(jù)Activity的啟動流程,看看過程中是如何檢索包信息來實現(xiàn)啟動的,根據(jù)已知原理,我們到ActivityStarter的execute中開始尋找,我們發(fā)現(xiàn)啟動的所有信息,包括ActivityInfo等都是通過ActicityStarter.Request來指定的,顯然這是我們需要關(guān)注的重點,追蹤一下它的信息都是怎么來的。

  • mRequest.resolveActivity(mSupervisor)

    在execute方法中有一句:

    if (mRequest.activityInfo == null) {
        mRequest.resolveActivity(mSupervisor);
    }
    

    因為經(jīng)查找,我們并沒有發(fā)現(xiàn)activityInfo被設(shè)置了,因此這里會執(zhí)行mRequest.resolveActivity方法。

    void resolveActivity(ActivityTaskSupervisor supervisor) {
        ...
        resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
        ...
        activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
                profilerInfo);
          ...
    }
    

    mSupervisor是ActivityTaskSupervisor,它的resolveActivity方法中:

    mService.getPackageManagerInternalLocked().resolveIntent(
            intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
            filterCallingUid);
    

    mService是ActivityTaskManagerService,它的getPackageManagerInternalLocked方法如下:

    PackageManagerInternal getPackageManagerInternalLocked() {
        if (mPmInternal == null) {
            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
        }
        return mPmInternal;
    }
    

    全局搜索“LocalServices.addService(PackageManagerInternal”會在PackageManagerService的構(gòu)造方法中找到:

    LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
    

    PackageManagerInternalImpl的resolveIntent方法在其父類PackageManagerInternalBase中定義:

    public final ResolveInfo resolveIntent(Intent intent, String resolvedType,
            @PackageManager.ResolveInfoFlagsBits long flags,
            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
            boolean resolveForStart, int filterCallingUid) {
        return getResolveIntentHelper().resolveIntentInternal(snapshot(),
                intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
                filterCallingUid);
    }
    

    ResolveIntentHelper的resolveIntentInternal方法如下:

    public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
            @PackageManager.ResolveInfoFlagsBits long flags,
            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
            boolean resolveForStart, int filterCallingUid) {
        try {
            ...
            final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
                    resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
                    resolveForStart, true /*allowDynamicSplits*/);
            ...
            final ResolveInfo bestChoice = chooseBestActivity(computer, intent, resolvedType, flags, privateResolveFlags, query, userId, queryMayBeFiltered);
            ...
            return bestChoice;
        } ...
    }
    

    computer是前面snapshot()方法返回的對象:

    @Override
    public final Computer snapshot() {
        return mService.snapshotComputer();
    }
    

    mService是PackageManagerService,它的snapshotComputer方法會返回一個ComputerEngine對象,computer就是它。

    ComputerEngine的queryIntentActivitiesInternal方法如下:

    public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
            int filterCallingUid, int userId, boolean resolveForStart,
            boolean allowDynamicSplits) {
        ...
        ComponentName comp = intent.getComponent();
        ...
        List<ResolveInfo> list = Collections.emptyList();
          //顯示啟動,指定目標(biāo)Activity類名形式的跳轉(zhuǎn)
        if (comp != null) {
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                ...
                final ResolveInfo ri = new ResolveInfo();
                ri.activityInfo = ai;
                  //可見這里最終返回只含有一個ResolveInfo的集合
                list = new ArrayList<>(1);
                list.add(ri);
                PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
                        mInjector.getCompatibility(), mComponentResolver,
                        list, false, intent, resolvedType, filterCallingUid);
            }
        } else {
              //隱式啟動,匹配action、type、data等形式的跳轉(zhuǎn),這種方式下list可能有多個匹配的
            ...
            //queryIntentActivitiesInternalBody開始查找匹配項
            //tip:解析匹配uri、action的邏輯在IntentResolver的queryIntent方法中
            QueryIntentActivitiesResult lockedResult =
                    queryIntentActivitiesInternalBody(
                            intent, resolvedType, flags, filterCallingUid, userId,
                            resolveForStart, allowDynamicSplits, pkgName, instantAppPkgName);
            ...
            if (lockedResult.sortResult) {
                  //按照優(yōu)先級排序,優(yōu)先級越高越靠前
                lockedResult.result.sort(RESOLVE_PRIORITY_SORTER);
            }
            list = lockedResult.result;
            }
        }
          ...
        return list;
    }
    

    這里根據(jù)Intent的查找方式分成了兩種,一種是Intent(this, destActivity.class) 這種指定了具體目標(biāo)Activity的顯示啟動,另一種是類似intent.setAction() 這種泛化匹配的隱式啟動,對于前者來說只會查到一個ActivityInfo,因此返回的集合中只會有一個ResolveInfo;后者可能會匹配到多個ActivityInfo,返回的集合中可能會有多個ResolveInfo。

    回到resolveIntentInternal方法,接下來會調(diào)用chooseBestActivity方法:

    private ResolveInfo chooseBestActivity(Computer computer, Intent intent, String resolvedType,
            @PackageManager.ResolveInfoFlagsBits long flags,
            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
            List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
        if (query != null) {
            final int n = query.size();
              //對于顯示啟動來說只是取出list中唯一的ResolveInfo
            if (n == 1) {
                return query.get(0);
            } else if (n > 1) {
                  //對于隱式啟動
                final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
                //如果第一個匹配項有著更高且唯一的優(yōu)先級,也就是說第二個匹配項必須低于第一個(之前排好序了)
                ResolveInfo r0 = query.get(0);
                ResolveInfo r1 = query.get(1);
                ...
                if (r0.priority != r1.priority
                        || r0.preferredOrder != r1.preferredOrder
                        || r0.isDefault != r1.isDefault) {
                    return query.get(0);
                }
                //如果優(yōu)先級篩選沒有決出唯一的一個ResolveInfo則先嘗試獲取首選項(本地保存記錄)
                ResolveInfo ri = mPreferredActivityHelper.findPreferredActivityNotLocked(computer,
                        intent, resolvedType, flags, query, true, false, debug,
                        userId, queryMayBeFiltered);
                  
                if (ri != null) {
                    return ri;
                }
                ...
                //mResolveInfoSupplier.get()會獲取一個默認(rèn)的(ResolverActivity)或者自定義的Activity用于讓用戶選擇哪一個Activity
                ri = new ResolveInfo(mResolveInfoSupplier.get());
                ri.activityInfo = new ActivityInfo(ri.activityInfo);
                ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
                final String intentPackage = intent.getPackage();
                  //如果所有的目標(biāo)Activity都來自同一個package的話則直接使用第一個的ApplicationInfo
                if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
                    final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
                    ri.resolvePackageName = intentPackage;
                    if (mUserNeedsBadging.get(userId)) {
                        ri.noResourceId = true;
                    } else {
                        ri.icon = appi.icon;
                    }
                    ri.iconResourceId = appi.icon;
                    ri.labelRes = appi.labelRes;
                }
                  //如果多個目標(biāo)Activity來自不同的包則使用mResolveInfoSupplier.get()的ApplicationInfo
                ri.activityInfo.applicationInfo = new ApplicationInfo(
                        ri.activityInfo.applicationInfo);
                if (userId != 0) {
                    ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
                            UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
                }
                //汽車應(yīng)用相關(guān)
                  // Make sure that the resolver is displayable in car mode
                if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
                ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
                return ri;
            }
        }
      return null;
    }
    

    首選項是用戶之前選擇過的默認(rèn)項,mPreferredActivityHelper.findPreferredActivityNotLocked最終是從pm.Settings中的mPreferredActivities中根據(jù)userId來獲取PreferredIntentResolver實例,如果沒取到則使用mResolveInfoSupplier.get()獲取,來源邏輯如下:

    //PackageManagerService:
    //倒數(shù)第二個參數(shù),所以get()獲取的是mResolveInfo
    mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
                    injector.getCompatibility(), mUserManager, mDomainVerificationManager,
                    mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
    

    mResolveInfo有兩個途徑賦值,一個是在setPlatformPackage方法中:

    void setPlatformPackage(AndroidPackage pkg, PackageSetting pkgSetting) {
        synchronized (mLock) {
            // Set up information for our fall-back user intent resolution activity.
            mPlatformPackage = pkg;
    
            // The instance stored in PackageManagerService is special cased to be non-user
            // specific, so initialize all the needed fields here.
            mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
                    PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
    
            if (!mResolverReplaced) {
                mResolveActivity.applicationInfo = mAndroidApplication;
                mResolveActivity.name = ResolverActivity.class.getName();
                ...//mResolveActivity的其他信息設(shè)置
                mResolveInfo.activityInfo = mResolveActivity;
                ...
                mResolveComponentName = new ComponentName(
                        mAndroidApplication.packageName, mResolveActivity.name);
            }
            PackageManagerService.onChanged();
        }
        applyUpdatedSystemOverlayPaths();
    }
    

    setPlatformPackage方法是在InstallPackageHelper的commitPackageSettings方法中調(diào)用,而commitPackageSettings方法是在之前的安裝流程中通過commitPackagesLocked -> commitReconciledScanResultLocked方法中調(diào)用的:

    if (pkg.getPackageName().equals("android")) {
        mPm.setPlatformPackage(pkg, pkgSetting);
    }
    

    可見,這是系統(tǒng)包的默認(rèn)配置,也就是說對于多個匹配目標(biāo)的情況下系統(tǒng)默認(rèn)會提供一個可供用戶選擇的Activity,而在setPlatformPackage方法中我們可以看到,這個Activity就是ResolverActivity。

    mResolveInfo的另一個途徑是自定義途徑,提供一個可供廠商自定義此Activity的入口,通過setUpCustomResolverActivity方法賦值:

    void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
      synchronized (mLock) {
            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
                  PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);
            // Set up information for custom user intent resolution activity.
            mResolveActivity.applicationInfo = appInfo;
            mResolveActivity.name = mCustomResolverComponentName.getClassName();
            mResolveActivity.packageName = pkg.getPackageName();
            mResolveActivity.processName = pkg.getProcessName();
            mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
            mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
                  | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
              mResolveInfo.activityInfo = mResolveActivity;
            ...
        }
    }
    

    可以看到,mResolveActivity.name的值來自mCustomResolverComponentName。setUpCustomResolverActivity方法同樣是在commitPackageSettings方法中調(diào)用的:

    final String pkgName = pkg.getPackageName();
    if (mPm.mCustomResolverComponentName != null
            && mPm.mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
        mPm.setUpCustomResolverActivity(pkg, pkgSetting);
    }
    

    mCustomResolverComponentName是在PackageManagerService構(gòu)造方法中賦值的:

    String customResolverActivityName = Resources.getSystem().getString(
            R.string.config_customResolverActivity);
    if (!TextUtils.isEmpty(customResolverActivityName)) {
        mCustomResolverComponentName = ComponentName.unflattenFromString(
                customResolverActivityName);
    }
    

    可見,廠商是通過配置config_customResolverActivity屬性來設(shè)置自定義的“ResolverActivity”。

    因為系統(tǒng)package的安裝要優(yōu)先于其他package,因此setPlatformPackage會優(yōu)先設(shè)置,之后廠商package如果符合條件時setUpCustomResolverActivity才會被調(diào)用,他們都是設(shè)置的mResolveInfo,所以如果廠商設(shè)置了自定義“ResolverActivity”的話則會覆蓋掉系統(tǒng)默認(rèn)的ResolverActivity。

    ResolverActivity是如何發(fā)揮作用的,因為也沒看到把查詢到的所有匹配的ActivityInfo數(shù)據(jù)傳遞給它???我們?nèi)?strong>ResolverActivity(用的是9.0的源碼)中去看看有沒有答案。

    通過查看其布局文件resolver_list_with_default.xml(其中之一的情況,無所謂這里只需要了解個大概界面)得知,它含有一個ListView來盛放所有匹配的app項,我們找到了它的adapter設(shè)置的內(nèi)部類ItemClickListener,在其onItemClick方法中會調(diào)用startSelected方法:

    public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
        ...
        //從其data中取出TargetInfo
        TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered);
        if (target == null) {
            return;
        }
        if (onTargetSelected(target, always)) {
            ...
            finish();
        }
    }
    

    onTargetSelected方法如下:

    protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
        final ResolveInfo ri = target.getResolveInfo();
        final Intent intent = target != null ? target.getResolvedIntent() : null;
          //保存本次選中的啟動首選項
        if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem())
                && mAdapter.mUnfilteredResolveList != null) {
            IntentFilter filter = new IntentFilter();
            ...//根據(jù)intent的action、data、category等生成intentFilter
              //IntentFilter在這里就是用于本次啟動的匹配信息的,以便再次啟動時復(fù)用
            if (filter != null) {
                ...//一些保存信息的生成        
                //保存選擇記錄
                //tip:IPackageManager的實現(xiàn)類是IPackageManagerBase
                if (alwaysCheck) {
                      ...
                    //即調(diào)用IPackageManager的addPreferredActivity
                    final PackageManager pm = getPackageManager();
                    pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
                      ...
                } else {
                      //最終也是調(diào)用IPackageManager的addPreferredActivity
                      //tip:ResolveListController.setLastChosen方法中調(diào)用AppGlobals.getPackageManager().setLastChosenActivity方法,AppGlobals.getPackageManager()獲取的是IPackageManager
                    mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
                }
            }
        }
          //啟動本次選中的目標(biāo)Activity
        if (target != null) {
            safelyStartActivity(target);
        }
        return true;
    }
    

    這個方法里會先保存本次選中的啟動首選項,還記得之前chooseBestActivity中的PreferredActivityHelper.findPreferredActivityNotLocked操作嗎?這里的PackageManager的addPreferredActivity操作與之呼應(yīng)上了。最后啟動選中的目標(biāo)Activity。

    流程對上了,我們的重點來到了target是怎么來的,它來自targetInfoForPosition方法,這個方法只是根據(jù)index從ResolverListAdapter的mDisplayList中返回,mDisplayList是怎么來的呢?它通過addResolveInfo方法添加,這個方法是由onCreate->configureContentView->mAdapter.rebuildList()->finishRebuildingListWithFilteredResults->processSortedList(currentResolveList)調(diào)用的:

    private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
      for (int i = 1; i < sortedComponents.size(); i++) {
         ...
         ResolvedComponentInfo rci = sortedComponents.get(i);
         ...
         processGroup(sortedComponents, start, (i - 1), rci, r0Label);
         ...
         start = i;
      }
    }
    

    processGroup是去除重復(fù)的項,并且調(diào)用addResolveInfoWithAlternates方法,這個方法里再調(diào)用addResolveInfo方法添加到mDisplayList中:

    private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
            CharSequence extraInfo, CharSequence roLabel) {
        ...
        final Intent intent = rci.getIntentAt(0);
        final ResolveInfo add = rci.getResolveInfoAt(0);
        final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
        final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
                extraInfo, replaceIntent);
        ...
        addResolveInfo(dri);
        ...
    }
    

    繼續(xù)看上面processSortedList方法的sortedComponents方法的來源,它來自finishRebuildingListWithFilteredResults方法的傳入?yún)?shù)?;氐絉esolverListAdapter的rebuildList方法:

    protected boolean rebuildList(boolean doPostProcessing) {
        mDisplayList.clear();
        List<ResolvedComponentInfo> currentResolveList = getInitialRebuiltResolveList();
          ...
        boolean result =
                finishRebuildingListWithFilteredResults(currentResolveList, doPostProcessing);
        return result;
    }
    

    可以看到,傳入?yún)?shù)是currentResolveList,由getInitialRebuiltResolveList方法返回:

    List<ResolvedComponentInfo> getInitialRebuiltResolveList() {
        if (mBaseResolveList != null) {
            List<ResolvedComponentInfo> currentResolveList = new ArrayList<>();
            mResolverListController.addResolveListDedupe(currentResolveList,
                    mResolverListCommunicator.getTargetIntent(),
                    mBaseResolveList);
            return currentResolveList;
        } else {
            return mResolverListController.getResolversForIntent(
                            /* shouldGetResolvedFilter= */ true,
                            mResolverListCommunicator.shouldGetActivityMetadata(),
                            mIntents);
        }
    }
    

    mResolverListController.getResolversForIntent最底層是調(diào)用了mpm.queryIntentActivitiesAsUser方法,mpm查看來源發(fā)現(xiàn)是通過ContextImpl的getPackageManager返回的ApplicationPackageManager對象,ApplicationPackageManager的queryIntentActivitiesAsUser方法調(diào)用了IPackageManagerBase的queryIntentActivities方法拿到一個ParceledListSlice<ResolveInfo>,然后調(diào)用它的getList()把它的mList返回了,所以我們的重點來到了queryIntentActivities方法:

    public final @NonNull
    ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
        return new ParceledListSlice<>(snapshot().queryIntentActivitiesInternal(intent,
                    resolvedType, flags, userId));
    }
    

    傳給ParceledListSlice構(gòu)造方法的正是賦值給它的mList的,snapshot()我們之前已經(jīng)知道它是ComputerEngine,它的queryIntentActivitiesInternal方法熟悉吧,正是我們開啟這一切的起點,終于真相大白了,之所以chooseBestActivity方法中針對多匹配的情況時沒有把所有的匹配項都傳給ResolverActivity,是因為在ResolverActivity中會再次調(diào)用queryIntentActivitiesInternal查詢匹配項。

    Ok!順帶著我們已經(jīng)搞明白了啟動Activity時對于隱式啟動的處理邏輯,我們下面要回到主任務(wù)中,畢竟我們的目標(biāo)是分析啟動信息是怎么獲取的。

  • getActivityInfo

    上面的邏輯只是針對ActivityInfo的處理邏輯,那么ActivityInfo是怎么來的呢?

    ComputerEngine的getActivityInfo方法最終調(diào)用了getActivityInfoInternalBody方法:

    protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
        ParsedActivity a = mComponentResolver.getActivity(component);
        AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
        if (pkg != null && mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {
            PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
            if (ps == null) return null;
            if (shouldFilterApplication(
                    ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
                return null;
            }
            return PackageInfoUtils.generateActivityInfo(pkg,
                    a, flags, ps.getUserStateOrDefault(userId), userId, ps);
        }
          //如果上面沒返回,則看是否是“ResolverActivity”
        if (resolveComponentName().equals(component)) {
            return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity,
                    flags, PackageUserStateInternal.DEFAULT, userId);
        }
          //都不是則返回null
        return null;
    }
    

    mComponentResolver類型是ComponentResolverApi,我們到PackageManagerService的main方法中的injector的構(gòu)造邏輯中找到了它的實現(xiàn)類ComponentResolver,它的getActivity方法定義在其父類ComponentResolverLocked中,內(nèi)部又是調(diào)用了super的getActivity方法,也就是ComponentResolverBase:

    //ComponentResolverBase:
    @Override
    public ParsedActivity getActivity(@NonNull ComponentName component) {
        return mActivities.mActivities.get(component);
    }
    

    mActivities中的數(shù)據(jù)是怎么來的?第一個mActivities是ComponentResolver.ActivityIntentResolver,它的mActivities是ArrayMap<ComponentName, ParsedActivity>,這個ArrayMap通過它的addActivity方法添加數(shù)據(jù),這個addActivity方法又是被ComponentResolver.ActivityIntentResolver的addActivitiesLocked調(diào)用的:

    private void addActivitiesLocked(AndroidPackage pkg,
            List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
        final int activitiesSize = ArrayUtils.size(pkg.getActivities());
        ...
        for (int i = 0; i < activitiesSize; i++) {
            ParsedActivity a = pkg.getActivities().get(i);
            mActivities.addActivity(a, "activity", newIntents);
            ...
        }
        ...
    }
    

    該方法又被ComponentResolver的addAllComponents方法調(diào)用,而addAllComponents又在PackageManagerService的commitPackageSettings方法中被調(diào)用,這正是我們之前分析的安裝流程中的一個必執(zhí)行方法。

    獲取到ParsedActivity之后,再根據(jù)它的packageName去mPackages中獲取AndroidPackage,而mPackages是在InstallPackageHelper的commitPackageSettings方法中通過mPm.mPackages直接添加數(shù)據(jù)的:

    mPm.mPackages.put(pkg.getPackageName(), pkg);
    

    AndroidPackage中的activity信息是怎么來的呢?

    那我們得再回到之前安裝流程中找一下AndroidPackage是怎么來的,AndroidPackage通過parsedPackage.hideAsFinal()獲取,parsedPackage又通過PackageParser2的parsePackage方法獲?。?/p>

    public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
            List<File> frameworkSplits) throws PackageManagerException {
        ...
        ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
                frameworkSplits);
        ...
        ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
        return parsed;
    }
    
    public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
            List<File> frameworkSplits) {
        if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
                && frameworkSplits.size() > 0
                && packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
            return parseClusterPackage(input, packageFile, frameworkSplits, flags);
        } else if (packageFile.isDirectory()) {
            return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
        } else {
            return parseMonolithicPackage(input, packageFile, flags);
        }
    }
    
    private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
            List<File> frameworkSplits, int flags) {
        ...
        final ParseResult<PackageLite> liteResult =
                ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits,
                        liteParseFlags);
        ...
        try {
            final File baseApk = new File(lite.getBaseApkPath());
            final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
                    lite.getPath(), assetLoader, flags);
            if (result.isError()) {
                return input.error(result);
            }
    
            ParsingPackage pkg = result.getResult();
            ...
            return input.success(pkg);
        } ...
    }
    

    parseClusterPackage中會調(diào)用ApkLiteParseUtils.parseClusterPackageLite方法,該方法中會遍歷apkDirectory下的所有apk文件,對每個apk文件調(diào)用parseApkLite方法,最終是調(diào)用parseApkLiteInner方法:

    private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
            File apkFile, FileDescriptor fd, String debugPathName, int flags) {
        final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
        XmlResourceParser parser = null;
          //這里會使用ApkAssets加載apk文件
        ApkAssets apkAssets = null;
        try {
            try {
                apkAssets = fd != null
                        ? ApkAssets.loadFromFd(fd, debugPathName, 0 /* flags */, null /* assets */)
                        : ApkAssets.loadFromPath(apkPath);
            } ...
                  //ANDROID_MANIFEST_FILENAME是AndroidManifest.xml
            parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
                  ...
            return parseApkLite(input, apkPath, parser, signingDetails, flags);
        } ...
    }
    

    調(diào)用parseApkLite方法來解析AndroidManifest.xml配置,下面只貼部分代碼以明白大概做了什么即可:

    private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
            XmlResourceParser parser, SigningDetails signingDetails, int flags)
            throws IOException, XmlPullParserException {
          //解析manifest標(biāo)簽下的split屬性、package屬性
        ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
          ...
        Pair<String, String> packageSplit = result.getResult();
          ...
        //安裝位置
        int installLocation = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE,
                "installLocation", PARSE_DEFAULT_INSTALL_LOCATION);
          //versionCode
        int versionCode = parser.getAttributeIntValue(ANDROID_RES_NAMESPACE, "versionCode", 0);
        ...
        int type;
        final int searchDepth = parser.getDepth() + 1;
        final List<VerifierInfo> verifiers = new ArrayList<>();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
            ...
            if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
                ...
            } else if (TAG_APPLICATION.equals(parser.getName())) {
                ...//解析application標(biāo)簽
            } else if...
              } else if (TAG_USES_SDK.equals(parser.getName())) {
                //sdk版本相關(guān)
                String minSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
                        "minSdkVersion");
                String targetSdkVersionString = parser.getAttributeValue(ANDROID_RES_NAMESPACE,
                        "targetSdkVersion");
                          ...
            }
        }
          ...
        return input.success(new ApkLite(codePath, ...));
    }
    

    parseApkLite主要是解析出一些大方面的配置,和系統(tǒng)相關(guān)的,比如安裝位置、版本號、sdk信息等,而像應(yīng)用本身的name、label、icon等其他特性信息會在隨后的parseBaseApk方法中解析:

    private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
            String codePath, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        ...
        final String pkgName;
          ...
        final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
        try {
            final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
                    "coreApp",false);
            final ParsingPackage pkg = mCallback.startParsingPackage(
                    pkgName, apkPath, codePath, manifestArray, isCoreApp);
            final ParseResult<ParsingPackage> result =
                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
            if (result.isError()) {
                return result;
            }
    
            return input.success(pkg);
        } finally {
            manifestArray.recycle();
        }
    }
    

    mCallback.startParsingPackage返回的是PackageImpl,是AndroidPackage的子類,這也就是最終要返回的的AndroidPackage對象。parseBaseApkTags方法中會解析出和應(yīng)用本身相關(guān)的信息:

    private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
            TypedArray sa, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        ...
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            String tagName = parser.getName();
            final ParseResult result;
            if (TAG_APPLICATION.equals(tagName)) {
                  ...
                //解析<application>信息以及子標(biāo)簽信息,比如注冊的四大組件<activity>、<service>等
                result = parseBaseApplication(input, pkg, res, parser, flags);
                ...
            } else {
                  //解析和application同級的其他標(biāo)簽,比如<uses-permission>等
                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
            }
              ...
        }
          ...
        return input.success(pkg);
    }
    

    parseBaseApplication方法如下:

    private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        final String pkgName = pkg.getPackageName();
        int targetSdk = pkg.getTargetSdkVersion();
    
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
        ...//application標(biāo)簽的信息解析,比如name、label等
    
        //子標(biāo)簽信息的解析
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
    
            final ParseResult result;
            String tagName = parser.getName();
            switch (tagName) {
                case "activity":
                    isActivity = true;
                case "receiver":
                    ParseResult<ParsedActivity> activityResult =
                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                    res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
                                    input);
    
                    if (activityResult.isSuccess()) {
                          //拿到的是ParsedActivityImpl
                        ParsedActivity activity = activityResult.getResult();
                          //添加到ParsingPackageImpl中的activities集合中
                        pkg.addActivity(activity);
                    }
    
                    result = activityResult;
                    break;
                case "service":
                    ParseResult<ParsedService> serviceResult =
                            ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
                                    flags, sUseRoundIcon, null /*defaultSplitName*/,
                                    input);
                    if (serviceResult.isSuccess()) {
                        ParsedService service = serviceResult.getResult();
                        hasServiceOrder |= (service.getOrder() != 0);
                        pkg.addService(service);
                    }
    
                    result = serviceResult;
                    break;
                ...
                default:
                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                    break;
            }
                  ...
        }
          ...
        return input.success(pkg);
    }
    

    可以看到,activity和receiver組件都是通過ParsedActivityUtils.parseActivityOrReceiver方法解析的,具體邏輯就不看了,沒什么特別的。

    總結(jié)一下,在安裝過程中會解析apk的AndroidManifest.xml文件,然后把解析的activity信息保存到AndroidPackage(實際類型是其子類PackageImpl)的activities集合(在其父類ParsingPackageImpl中定義)中,然后在隨后的commitPackageSettings方法流程中調(diào)用addAllComponents方法將所有的activity信息交給PackageManagerService中的mComponentResolver(ComponentResolver),之后在啟動Activity時調(diào)用getActivity方法就會使用mComponentResolver獲取AndroidPackage(也就是PackageImpl),最后調(diào)用PackageInfoUtils.generateActivityInfo方法生成ActivityInfo。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容