編輯:關於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; jsystemActivities = 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 ListqueryIntentActivities(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 ParceledListSlicequeryIntentActivities(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 ListqueryIntent(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 ListqueryIntent(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, FastImmutableArraySetcategories, 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, Listdest) { 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有許多相似之處。
先看看效果圖:首先是布局文件<FrameLayout android:layout_width=match_parent android:layout_margin
本文實例講述了Android編程之canvas繪制各種圖形的方法。分享給大家供大家參考,具體如下:1、首先說一下canvas類:Class OverviewThe Can
華為榮耀X2手機系統版本更新到B013版以後,鎖屏和密碼中增加了一項智能解鎖功能,聽起來都很高大上的功能,比指紋解鎖還NB,那這個智能解鎖功能怎麼用呢?下面
anddroid studio的內存修改昨天有位朋友問到了下面的一個問題這個判斷為android studio的分配的內存不夠用。據我的了解造成這個的原因主要有以下幾個方