Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android7.0 PackageManagerService (4) Intent匹配Activity的過程

Android7.0 PackageManagerService (4) Intent匹配Activity的過程

編輯:關於Android編程

通過前面的分析,我們知道PKMS負責維護終端全部的Package信息,因此可以想到PKMS具有能力對外提供統一的Package信息查詢接口。
我們以查詢匹配指定Intent的所有Activity的過程為例,分析一下PKMS提供這類服務對應的流程。

一、PKMS中Activity的信息管理
在前面的博客已經提到,PKMS解析Package信息時,將調用到scanPackageDirtyLI函數。
該函數將會將Package中四大組件等信息,加入到PKMS定義的數據結構中,以便統一管理。

以下代碼片段就是為了將Package中的activity相關的信息,加入到PKMS中:

N = pkg.activities.size();
r = null;
for (i=0; i<n; ..........="" a="pkg.activities.get(i);" a.info.processname="fixProcessName(pkg.applicationInfo.processName," packageparser.activity="" pre="">

上述代碼的內容比較簡單,主要需要關注對應的數據結構。 結合代碼,我們知道: 1、PKMS中的成員變量mActivities為ActivityIntentResolver類型,用於保存所有與Activity相關的信息。ActivityIntentResolver內部也有一個mActivities變量,它以ComponentName為key,保存PackageParser.Activity對象。 2、從APK文件中解析出來的所有和Activity相關的信息都由PackageParser.Activity來保存。

我們跟進一下ActivityIntentResolver的addActivity函數:

public final void addActivity(PackageParser.Activity a, String type) {
    //將ComponentName和Activity保存到ActivityIntentResolver的mActivities中
    mActivities.put(a.getComponentName(), a);
    ..........
    final int NI = a.intents.size();
    for (int j=0; j systemActivities =
                    ps != null && ps.pkg != null ? ps.pkg.activities : null;
            //按照策略,將一些IntentFilter的優先級設置為0
            //例如非系統APK的優先級將被設置為0
            adjustPriority(systemActivities, intent);
        }
        ..........
        //將activity中對應的IntentFilter加入到PKMS中
        addFilter(intent);
    }
}

從上面的代碼,我們知道了PKMS除了維護Activity的基本信息外,重點“照顧”了Activity定義的IntentFilter。 跟進addFilter函數:

public void addFilter(F f) {
    .......
    //mFilters保存所有的IntentFilter信息
    mFilters.add(f);

    //除此之外,為了加快匹配工作的速度,PKMS還分類保存了IntentFilter的信息

    //mSchemeToFilter用於保存uri中與scheme相關的IntentFilter
    int numS = register_intent_filter(f, f.schemesIterator(),
                mSchemeToFilter, "      Scheme: ");

    //按照MIME type存儲IntentFilter,分別定義了mTypeToFilter、mBaseTypeToFilter和mWildTypeToFilter
    //與MIME的格式有關,代碼較為簡單,不深入分析
    int numT = register_mime_types(f, "      Type: ");

    if (numS == 0 && numT == 0) {
        //mActionToFilter用於保存僅設置了Action條件的IntentFilter
        register_intent_filter(f, f.actionsIterator(),
                mActionToFilter, "      Action: ");
    }
    if (numT != 0) {
        //mTypedActionToFilter用於保存既設置了Action條件,又設置了Data類型的IntentFilter
        register_intent_filter(f, f.actionsIterator(),
                mTypedActionToFilter, "      TypedAction: ");
    }
}

二、PKMS中Intent與Activity的匹配過程 一般情況下,客戶端可以使用ApplicationPackageManager的接口queryIntentActivities發起查詢請求:

public List queryIntentActivities(Intent intent, int flags) {
    return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId());
}

public List queryIntentActivitiesAsUser(Intent intent,
        int flags, int userId) {
    try {
        ParceledListSlice parceledList =
                //調用PKMS的接口
                mPM.queryIntentActivities(intent,
                        //如果Intent的Data中包含Uri,那麼就需要根據Uri對應的ContentProvider得到Data對應的類型
                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                        flags, userId);
        if (parceledList == null) {
            return Collections.emptyList();
        }
        return parceledList.getList();
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

我們主要分析PKMS的queryIntentActivities函數:

public @NonNull ParceledListSlice queryIntentActivities(Intent intent,
        String resolvedType, int flags, int userId) {
    try {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");

        return new ParceledListSlice<>(
                queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

private @NonNull List queryIntentActivitiesInternal(Intent intent,
        String resolvedType, int flags, int userId) {
    if (!sUserManager.exists(userId)) return Collections.emptyList();
    .........
    ComponentName comp = intent.getComponent();
    .........
    if (comp == null) {
        if (intent.getSelector() != null) {
            intent = intent.getSelector();
            comp = intent.getComponent();
        }
    }

    //顯示匹配
    if (comp != null) {
        final List list = new ArrayList(1);
        //利用getActivityInfo獲取指定的ActivityInfo即可
        final ActivityInfo ai = getActivityInfo(comp, flags, userId);
        if (ai != null) {
            final ResolveInfo ri = new ResolveInfo();
            ri.activityInfo = ai;
            list.add(ri);
        }
        return list;
    }

    synchronized (mPackages) {
        final String pkgName = intent.getPackage();
        //Intent未指定Package名,需要在整個系統范圍內進行匹配查詢
        if (pkgName == null) {
            //處理隱式Intent,此處引入了CrossProfile和Current Profile的概念,不是很懂
            //但不論如何,最終調用的是ActivityIntentResolver的queryIntent函數來得到實際的結果
            ..............
        }

        //Intent指定了Package名
        final PackageParser.Package pkg = mPackages.get(pkgName);
        if (pkg != null) {
            return filterIfNotSystemUser(
                    //在指定Package的Activity中,進行匹配
                    mActivities.queryIntentForPackage(
                            intent, resolvedType, flags, pkg.activities, userId),
                            userId);
        }
        return new ArrayList();
    }
}

上面的代碼明顯的將Intent分為3類進行處理: 如果Intent指明了Component,則直接查詢該Component對應的ActivityInfo,這也是我們常說的顯示Intent; 如果Intent僅指明的Package名,則根據Package名找出對應的Package,然後再從中Package中找出匹配的ActivityInfo; 如果上述條件均不滿足,也就是遇到常說的隱式Intent,則需要在全局系統中查找ActivityInfo。

這裡我們重點分析一下,ActivityIntentResolver的queryIntent函數流程:

public List queryIntent(Intent intent, String resolvedType,
        boolean defaultOnly, int userId) {
    if (!sUserManager.exists(userId)) return null;
    mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
    //調用IntentResolver的queryIntent函數
    return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}

跟進IntentResolver的queryIntent函數:

public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
        int userId) {
    String scheme = intent.getScheme();

    //用於保存最終結果
    ArrayList finalList = new ArrayList();
    ...........

    F[] firstTypeCut = null;
    F[] secondTypeCut = null;
    F[] thirdTypeCut = null;
    F[] schemeCut = null;

    //以下先從各個維度進行初步篩選

    // If the intent includes a MIME type, then we want to collect all of
    // the filters that match that MIME type.
    if (resolvedType != null) {
        //從PKMS中的mTypeToFilter、mWildTypeToFilter、mBaseTypeToFilter和mTypedActionToFilter中
        //取出匹配類型的ActivityInfo填充到firstTypeCut、secondTypeCut和thirdTypeCut中
        ..................
    }

    // If the intent includes a data URI, then we want to collect all of
    // the filters that match its scheme (we will further refine matches
    // on the authority and path by directly matching each resulting filter).
    if (scheme != null) {
        schemeCut = mSchemeToFilter.get(scheme);
        ..............
    }

    // If the intent does not specify any data -- either a MIME type or
    // a URI -- then we will only be looking for matches against empty
    // data.
    if (resolvedType == null && scheme == null && intent.getAction() != null) {
        firstTypeCut = mActionToFilter.get(intent.getAction());
        ..............
    }

    FastImmutableArraySet categories = getFastIntentCategories(intent);

    //以下開始整合所有信息,進行一輪輪地篩選
    //每次都將符合全部要求的ActivityInfo加入到finalList中

    if (firstTypeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly,
                resolvedType, scheme, firstTypeCut, finalList, userId);
    }
    if (secondTypeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly,
                resolvedType, scheme, secondTypeCut, finalList, userId);
    }
    if (thirdTypeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly,
                resolvedType, scheme, thirdTypeCut, finalList, userId);
    }
    if (schemeCut != null) {
        buildResolveList(intent, categories, debug, defaultOnly,
                resolvedType, scheme, schemeCut, finalList, userId);
    }
    sortResults(finalList);
    ............
    return finalList;
}

上述代碼的主要目的比較清晰,最終就是通過Intent攜帶的信息,從PKMS中取出完全與之要求相匹配的ActivityInfo。 按照邏輯來講,queryIntent函數初始時,應該就是從不同的維度,將滿足該維度的ActivityInfo存入對應的Cut數組中。 那麼最終的結果,應該就是四個Cut數組的交集。

為了驗證這一點,我們看一下buildResolveList函數:

//src依次為每個Cut數組,dest為finalList
private void buildResolveList(Intent intent, FastImmutableArraySet categories,
        boolean debug, boolean defaultOnly,
        String resolvedType, String scheme, F[] src, List dest, int userId) {
    ...........
    for (i=0; i

我們看看allowFilterResult函數,實際上該函數由IntentResolver的子類實現,此處需要跟進ActivityInfoResolver中的實現:

protected boolean allowFilterResult(
        PackageParser.ActivityIntentInfo filter, List dest) {
    ActivityInfo filterAi = filter.activity.info;
    for (int i=dest.size()-1; i>=0; i--) {
        ActivityInfo destAi = dest.get(i).activityInfo;
        //匹配時返回false,將會跳過match檢查
        if (destAi.name == filterAi.name
                && destAi.packageName == filterAi.packageName) {
            return false;
        }
    }
    return true;
}

三、總結 從整個代碼來看,PKMS中查找與指定Intent匹配的Activity的過程,主要的思想很清晰,但其中涉及的數據結構和細節還是相當瑣碎的。

可以看出queryIntentActivities函數在處理隱式Intent時,主要通過IntentResolver來完成實際的查找工作。考慮到PKMS中四大組件的繼承結構,我們大體可以推測出Provider和Service的Intent匹配過程,應該和Activity有許多相似之處。

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