關於Intent以及IntentFilter的基本知識,你們能夠參閱以下資料,html
SDK中對Intent與IntentFilter的介紹 ---- 英文java
其中文翻譯以下:
android
我重點分析一下兩個方面:算法
第一部分 、Intent以及IntentFilter說明以及匹配規則分析數據結構
第二部分:Intent的解析過程分析app
想當初我看Intent相關知識時,對Intent、IntentFilter的理解就不好勁,總以爲系統定義了一個Intent,爲什麼還要整理個數據結構和算法
IntentFilter出來"禍害"廣大程序猿呢?但不解歸不解,在具體使用咱可不能含糊,因而只好依葫蘆畫瓢了,反正絕對還不錯。ide
* 它們是什麼 ?函數
* 它們的區別在哪兒 ?源碼分析
事實上,這兩個問題能夠概括爲Intent和Intent的主要功能是什麼 ? 你們能夠先捫心自問下,看看你的掌握程度如何 ?
個人理解以下:
* Intent : 主要功能是根據特定的條件找到匹配的組件,繼而對該組件執 行一些操做。好比執行startActivity()時,系統
首先要找到特定的Activity組件,而後執行onCreate()方法;startService()也得先找的特定的Service組件,而後執行
onCreate()或者onStart()方法 。
* IntentFilter :主要功能是爲某個組件向系統註冊一些特性(固然一個組件能夠註冊多個IntentFilter),以便Intent找到對應
的組件。
經過前面對Intent以及IntentFilter的分析,咱們很容易在語意上得出它們實際上是個先後關係。
* IntentFilter在前:任何一個組件必須先經過IntentFilter註冊。
* Intent 在後 :根據特定信息,找到以前以及註冊過的組件。
源碼分析:
Intent類源碼(部分) 路徑位於:\frameworks\base\core\java\android\content\Intent.java
[java] view plaincopyprint?
public class Intent implements Parcelable, Cloneable {
private String mAction; //action值
private Uri mData; //uri
private String mType; //MimeType
private String mPackage; //所在包名
private ComponentName mComponent; //組件信息
private int mFlags; //Flag標誌位
private HashSet<String> mCategories; //Category值
private Bundle mExtras; //附加值信息
//...
}
IntentFilter類源碼(部分) 路徑位於:\frameworks\base\core\java\android\content\IntentFilter.java
[java] view plaincopyprint?
public class IntentFilter implements Parcelable {
//...
//保存了全部action字段的值
private final ArrayList<String> mActions;
//保存了全部Category的值
private ArrayList<String> mCategories = null;
//保存了全部Schema(模式)的值
private ArrayList<String> mDataSchemes = null;
//保存了全部Authority字段的值
private ArrayList<AuthorityEntry> mDataAuthorities = null;
//保存了全部Path的值
private ArrayList<PatternMatcher> mDataPaths = null;
//保存了全部MimeType的值
private ArrayList<String> mDataTypes = null;
//...
}
PS :你們能夠參詳下Intent與IntentFilter類中不一樣字段的屬性類型。Intent中屬性類型基本上都是單個類型的,而IntentFilter
屬性都是集合類型的。從這方面思考,更能夠加深咱們的理解。
匹配種類有以下三種:
* 動做(Action)檢測
* 種類(Category)檢測
* 數據(Data & MimeType)檢測
比較好理解的是,進行匹配時Intent攜帶的Action字段值和Category字段值必須包含在IntentFilter中,不然匹配失敗。
SDK中說明的具體規則以下:
* 一個Intent對象既不包含URI,也不包含數據類型 ; 僅當過濾器也不指定任何URIs和數據類型時,才能經過檢測;
不然不能經過。
* 一個Intent對象包含URI,但不包含數據類型:僅當過濾器也不指定數據類型,同時它們的URI匹配,才能經過檢測。
例如,mailto:和tel:都不指定實際數據。
* 一個Intent對象包含數據類型,但不包含URI:僅當過濾也只包含數據類型且與Intent相同,才經過檢測。
* 一個Intent對象既包含URI,也包含數據類型(或數據類型可以從URI推斷出) ; 數據類型部分,只有與過濾器中之一
匹配纔算經過;URI部分,它的URI要出如今過濾器中,或者它有content:或file: URI,又或者過濾器沒有指定URI。
換句話說,若是它的過濾器僅列出了數據類型,組件假定支持content:和file: 。
PS :可別說我不會總結出來給你們分享,其實我以爲不少知識都須要本身去嘗試,去努力吸取,只要通過本身的消化,
學到的知識就是本身的了。
上面的規則比較生硬吧。咱們去源碼中去看看Intent與IntentFilter的具體匹配方法吧。
該方法是IntentFilter中的match()方法,該方法的內部處理邏輯就是按照上面的規則去判斷的,你們能夠仔細體味下,該方法
咱們在後面講到Intent解析過程時也會用到。 具體邏輯在代碼中進行了說明。
[java] view plaincopyprint?
public class IntentFilter implements Parcelable {
//匹配算法,,按照匹配規則進行
//Intent與該IntentFilter進行匹配時調用該方法參數表示Intent的相關屬性值
public final int match(String action, String type, String scheme,
Uri data, Set<String> categories, String logTag){
//首先、匹配Action字段
if (action != null && !matchAction(action)) {
if (Config.LOGV) Log.v(
logTag, "No matching action " + action + " for " + this);
return NO_MATCH_ACTION;
}
//其次、匹配數據(Uri和MimeType)字段
int dataMatch = matchData(type, scheme, data);
//...
//最後,匹配Category字段值
String categoryMatch = matchCategories(categories);
//...
}
//...
}
這部分咱們重點講解的知識點有以下:
一、Intent以及IntentFilter的主要職能
二、Intent與IntentFilter的關係
三、匹配規則說明
在繼續看本部分以前,但願您最好對PackageManagerService-----程序包管理服務有必定的認知(沒有也是OK的咯)。
能夠參考下面這篇問題去看看PackageManagerService的功能和相關流程。
老羅的博客:<<Android應用程序安裝過程源代碼分析>>
咱們知道Android源碼老是貼心的(不知道有沒有10086貼心),它對外提供了不少藉口供應用程序調用,例如AudioManger
(音頻管理)、TelephoneManger(電話管理)、一樣也提供了一個包管理-----PackageManager,經過它咱們能夠、獲取應用
程序包得信息,例如圖標、Lable標籤等。具體關於PackageManager的使用,能夠參考個人另一篇文章 :
<<Android中獲取應用程序(包)的信息-----PackageManager的使用(一)>>
2、PackageManagerService ---- 重量級選手
前面所說PackageManager不過是個傀儡,全部相關的操做都是由PackageManagerService 完成的。這兒咱們簡單的
分析下PackageManagerService 的特性:
①、開機就啓動,由SystemServer進程啓動 ;
②、啓動後它會掃描系統中全部應用程序Apk包下的AndroidManifest.xml文件,而後解析全部的
AndroidManifest.xml文件,繼而造成一個龐大的信息結構樹,而且保存在PackageManagerService 的相關屬性下。
它會掃描這兩個目錄:
/system/app ------------------> 系統應用程序
/data/app ------------------> 第三方應用程序(全部安裝的Apk包都會在該目錄下保存一份拷貝)
掃描完成後,因而全部的信息結構就構建了。PackageManagerService 的四個重要屬性以下:
[java] view plaincopyprint?
class PackageManagerService extends IPackageManager.Stub {
//...
//保存了全部Activity節點信息 。 自定義類
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
//保存了全部BroadcastReceiver節點信息 。 自定義類
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
//保存了全部Service節點信息。 。 自定義類
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
//保存了全部ContentProvider節點信息 , 以Hash值保存
// Keys are String (provider class name), values are Provider.
final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =
new HashMap<ComponentName, PackageParser.Provider>();
//...
}
值得注意這些屬性類型的不一樣。Activity、BroadcastReceiver、Service都採用了自定義類去保存相關信息,從類名上看,
類結構應該很類似。而ContentProvider只是簡單的採用了HashMap鍵值對去保存了信息? 莫非有錯 ? 咱們回憶下
AndroidManifest.xml定義組件信息時,Activity、BroadcastReceiver、Service均可以經過<intent-filter>去隱式匹配的,而
ContentProvider只須要一個Uri數據便可找到對應的ContentProvider組件信息了。 所以才採用了這兩種結構去保存信息。
其實咱們經過getPackageManager()方法得到的PackageManager對象,只是PackageManagerService的客戶端,
該客戶端類是ApplicationPackageManager,它是ContextIml類的內部類,顯然該類存在於用戶空間中。
源代碼(部分)以下:
[java] view plaincopyprint?
@Override
public PackageManager getPackageManager() {
//...
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
//...
}
class ContextImpl extends Context {
//...
/*package*/
static final class ApplicationPackageManager extends PackageManager{
//...
@Override
public ActivityInfo getActivityInfo(ComponentName className, int flags){
//...
}
}
//...
}
它與PackageManagerService的簡單關係以下:
三 、保存數據採用的數據結構和解析算法
毫無疑問,保存全部信息是一項很複雜的工程,在具體講解匹配過程時,咱們先看看系統爲了保存這些結構定義的一些
數據結構。
IntentInfo類:繼承至IntentFilter類
做用:保存了每一個<intent-filter>節點信息
ActivityIntentInfo類:繼承至IntentInfo類
做用:保存了<activity />節點下的< intent-filter>節點信息
ServiceIntentInfo:繼承至IntentInfo類
做用:保存了<service />節點下的< intent-filter >節點信息
Activity類:保存了<activity />節點信息
Service類:保存了<service />節點信息
PS:這些都是PackageParser類的內部類 。PackageParser的主要功能就是解析AndroidManifest.xml文件
IntentResolver類:模板類,父類,保存了<activity/>、<service/>、<receiver />節點的共同信息。
ActivityIntentResolver類:繼承至IntentResolver類。
做用:保存了全部<activity/>或者<receiver/>節點信息。(Activity或者BroadcastReceiver信息就是用該自定義類保存的)
ServiceIntentResolver類:繼承至IntentResolver類,保存了
做用:保存了全部<service/>節點信息。(Service信息就是用該自定義類保存的)。
一個簡單的UML圖表示以下:
不一樣的數據結構決定了不一樣的算法,而不一樣的算法又決定着性能,例如時間複雜度以及空間複雜度等。 在具體講解解析
採用的算法時,咱們先理解下這個情景。
假設一個女人決定參加一個相親節目(你們能夠理解成《非誠勿擾》),而後她向主辦方提出以下條件:
1、身高 ? 175cm以上 ;
二、 財富 ? 100萬 ;
三、 學歷 ? 本科以上 ;
……
主辦發經理一看,你丫的要求還真高。但客戶是萬能的,該經理也只能去找到知足這些條件的男人咯。
最開始,該經理是這麼想的,我把全部男的都給遍歷一遍,確定把知足這些條件的男人給揪出來。他找啊找,以爲這麼找下去
是否是太二B了(呵呵,你也是這麼想的嗎?)。經理就開始想:「我爲何不能根據這些條件把全部男的給分紅三個種羣呢?有錢
的男人在一塊兒,高個子的男人在一塊兒,高學歷的男人在一塊兒,這樣查找起來不是更快嗎 ? 「
因而,有了以下的劃分: PS, 你是屬於哪一類額 ?
二B算法(沒有分類) 高級點的算法(分類後)
可能你們對這種根據關鍵值分類的好處不能一目瞭然。咱們舉個通常例子吧:
假設當前共有100個男的。 其中有錢的有20人,高個子有30人,高學歷男人有10人。
根據第一種算法分類,咱們須要比較100次,而第二種算法咱們總共只須要比較60次。從整個基數來分析,算法確定優化了
最後,對不一樣的分類中查詢的結果進行組合從新排列下,便可獲得咱們的知足該女性的要求。
一樣的,在進行Intent匹配時,Android也採用了第二種方法來進行算法匹配。它根據一些關鍵值Action、MimeType、
Schema字段去進行分類。分類以後的集合大體以下:
因而在進行具體匹配時,咱們只是須要根據關鍵值從不一樣集合中獲取便可。
事實上,因爲MimeType的通配符(*)的特性,它的匹配能夠說是最難的。參考IntentResolver類,真正的關鍵值以下:
[java] view plaincopyprint?
//模板類 F類型可能爲ActivityIntentInfo或ServiceIntentInfo,R對象是ResolverInfo類
public class IntentResolver<F extends IntentFilter, R extends Object> {
//保存了全部<intent-filter>節點信息
//All filters that have been registered.
private final HashSet<F> mFilters = new HashSet<F>();
/** All of the MIME types that have been registered, such as "image/jpeg",
* "image/*", or "{@literal *}/*".
*/
//關鍵值表示MimeType形如: image/jpeg 、 image/*、/* 類型
private final HashMap<String, ArrayList<F>> mTypeToFilter
/**
* The base names of all of all fully qualified MIME types that have been
* registered, such as "image" or "*". Wild card MIME types such as
* "image/*" will not be here.
*/
//關鍵值表示MimeType形如:image、 image/*、* 類型
private final HashMap<String, ArrayList<F>> mBaseTypeToFilter
/**
* The base names of all of the MIME types with a sub-type wildcard that
* have been registered. For example, a filter with "image/*" will be
* included here as "image" but one with "image/jpeg" will not be
* included here. This also includes the "*" for the "{@literal *}/*"
* MIME type.
*/
//這個關鍵字段表示MimeType形如 :image、* 類型
private final HashMap<String, ArrayList<F>> mWildTypeToFilter
//All of the URI schemes (such as http) that have been registered.
//關鍵值字段表示Schema
private final HashMap<String, ArrayList<F>> mSchemeToFilter
/**
* All of the actions that have been registered, but only those that did
* not specify data.
*/
//關鍵值字段表示:Action
private final HashMap<String, ArrayList<F>> mActionToFilter
//All of the actions that have been registered and specified a MIME type.
//關鍵值字段表示:Action和MimeType。 即該<intent-filter>節點必須包含action和MimeType
private final HashMap<String, ArrayList<F>> mTypedActionToFilter
}
因而,經過這些關鍵字段咱們能夠去特定集合去查找,最後將結果在從新組合下,那不就萬事大吉了。
最後,咱們經過代碼走讀的方式,以一個查詢Activity的信息的方法,帶領你們去熟悉具體流程。該方法原型爲:
//經過給定的intent,查詢全部匹配的Activity組件信息
abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags)
PS:其實查詢Activity、Service、BroadcastReceiver的流程基本上是相同的。
Step 一、獲取PackageManager代理對象,調用該方法:
[java] view plaincopyprint?
PackageManager mPackageManger = this.getPackageManager() ;
//爲了說明,這兒咱們簡單查詢一個Intent對象,即全部應用程序的啓動Activity
Intent mainIntent = new Intent() ;
mainIntent.setAction(Intent.ACTION_MAIN);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mPackageManger.queryIntentActivities(mainIntent, 0);
//獲取PackageManager對象
@Override
public PackageManager getPackageManager() {
//...
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
}
Step 二、該PackageManager代理對象實則爲ApplicatonPackageManager 對象,該對象是ContextIml的內部類。
[java] view plaincopyprint?
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
try {
//mPM對象就是PackageManagerService的客戶端
return mPM.queryIntentActivities(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags);
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
Step 三、調用服務端PackageManagerService對象的對應方法。該方法位於PackageManagerService.java類中
[java] view plaincopyprint?
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags) {
//是否設置了組件ComponetName 信息
ComponentName comp = intent.getComponent();
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
ActivityInfo ai = getActivityInfo(comp, flags);
if (ai != null) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
synchronized (mPackages) {
//是否設置了包名
String pkgName = intent.getPackage();
if (pkgName == null) {
//調用mActivities去查詢
return (List<ResolveInfo>)mActivities.queryIntent(intent,
resolvedType, flags);
}
PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,
resolvedType, flags, pkg.activities);
}
return null;
}
}
首先、該方法判斷IntentComponentName是否存在,若是存在則爲顯示匹配了,直接返回特定組件相關信息;
其次、判斷是否設置了包名,即packageName,若是沒有指定包名,則查詢全部的應用程序包去找匹配的組件信息。若是
指定了packageName,則去指定包下去查找;
接着,調用特定的類繼續查找。因爲咱們找的是Activity組件信息,所以去ActivityIntentResolver類去查找。
Step 四、調用mActivities自定義類去查找
[java] view plaincopyprint?
//調用父類IntentResolver方法去查找
public List queryIntent(Intent intent, String resolvedType, int flags) {
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
該過程只是簡單的調用了父類IntentResolver去查找。
Step5 、進入IntentResolver類去真正的實現查找,該方法爲於IntentResolver.java類中。
[java] view plaincopyprint?
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
String scheme = intent.getScheme();
//用來保存查找到的組件信息,如Activity等
ArrayList<R> finalList = new ArrayList<R>();
//根據關鍵值去特定集合查詢到的一個可能結果
ArrayList<F> firstTypeCut = null;
ArrayList<F> secondTypeCut = null;
ArrayList<F> thirdTypeCut = null;
ArrayList<F> schemeCut = null;
//首先是否制定的數據類型 MimeType
// If the intent includes a MIME type, then we want to collect all of
// the filters that match that MIME type.
if (resolvedType != null) {
int slashpos = resolvedType.indexOf('/');
if (slashpos > 0) {
final String baseType = resolvedType.substring(0, slashpos);
if (!baseType.equals("*")) {
//匹配特定的MimeType
if (resolvedType.length() != slashpos+2|| resolvedType.charAt(slashpos+1) != '*') {
firstTypeCut = mTypeToFilter.get(resolvedType);
secondTypeCut = mWildTypeToFilter.get(baseType);
}
//...
}
}
//根據模式去匹配特定的集合
if (scheme != null) {
schemeCut = mSchemeToFilter.get(scheme);
}
//可能的話在去匹配Action所在集合
if (resolvedType == null && scheme == null && intent.getAction() != null) {
firstTypeCut = mActionToFilter.get(intent.getAction());
}
//對咱們前面經過關鍵字查詢的一個集合,在此循環遍歷匹配,將匹配到的結果保存在finalList集合中
if (firstTypeCut != null) {
buildResolveList(intent, debug, defaultOnly,
resolvedType, scheme, firstTypeCut, finalList);
}
if (secondTypeCut != null) {
buildResolveList(intent, debug, defaultOnly,
resolvedType, scheme, secondTypeCut, finalList);
}
if (thirdTypeCut != null) {
buildResolveList(intent, debug, defaultOnly,resolvedType, scheme, thirdTypeCut, finalList);
}
if (schemeCut != null) {
buildResolveList(intent, debug, defaultOnly,
resolvedType, scheme, schemeCut, finalList);
}
//根據IntentFilter的一些優先級進行排序
sortResults(finalList);
return finalList;
}
buildResolveList()方法的主要做用是將可能的集合在循環遍歷,將匹配的結果值保存在finalList集合中。
該方法爲於IntentResolver.java類中,方法原型以下:
[java] view plaincopyprint?
//經過前面關鍵字查找的可能集合,循環遍歷進行匹配,匹配成功就加入到dest集合中,即finalList集合中
private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly,
String resolvedType, String scheme, List<F> src, List<R> dest) {
Set<String> categories = intent.getCategories();
final int N = src != null ? src.size() : 0;
boolean hasNonDefaults = false;
int i;
for (i=0; i<N; i++) {
F filter = src.get(i);
int match;
//是否已經加入到匹配結果中去了,不容許重複添加
// Do we already have this one?
if (!allowFilterResult(filter, dest)) {
continue;
}
//調用Intent-filter方法去匹配該Intent信息
match = filter.match(
intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG);
//匹配成功,就存放在finalList集合中
if (match >= 0) {
if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
//調用子類的newResult()方法去返回一個ResolvInfo對象
final R oneResult = newResult(filter, match);
if (oneResult != null) {
dest.add(oneResult);
}
} else {
hasNonDefaults = true;
}
} else {
//...
}
}
//...
}
這個函數的邏輯判斷以下:
首先、經過給定的關鍵字去特定集合查詢一個可能的匹配集合,而後將這些集合信息保存在以下集合中:
ArrayList<F>firstTypeCut =null;
ArrayList<F>secondTypeCut =null;
ArrayList<F>thirdTypeCut =null;
ArrayList<F>schemeCut =null;
而後、連續四次調用buildResolveList()去進行匹配。每次調用結束後,參數finalList保存的是匹配結果的累加值,因此這
四次調用過程當中finalList集合包含的結果是一次累加的過程。固然了,四次連續調用buildResolveList()的次序能夠不分前後。
最後、調用sortResults()從匹配集合中進行一些排序等。
總結:第二部分經過重點介紹了PackageManagerService的功能匹配Intent採用的數據結構和算法,也只是簡單入了下門,希
望你們能參考源碼,認真理解Intent匹配過程。