intentfilter
一、注册方式
作为Android四大组件之一的广播有两种注册方式:静态注册和动态注册。在注册之前,我们应该有自己的BroadcastReceiver,即广播接收器,这样我们才能接收到广播,进行事务处理。
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onreceive(context context,intent intent) {
// 在此处处理事务
}
}
1、静态注册
指的是在Androidmanifest.xml中用<receiver>标签进行注册,并在标签内用<intent-filter>标签设置过滤器,例如<action>、<data>等。
<receiverandroid:name=".MyBroadCastReceiver">
<intent-filter>
<actionandroid:name="android.intent.action.PACKAGE_REMOVED"/>
<dataandroid:scheme="package"/>
</intent-filter>
</receiver>
2、动态注册
指的是在代码中进行注册,例如:
IntentFilterfilter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme(“package”);
当不再需要广播接收器时,要记得注销unregisterreceiver(),否则可能引起内存泄露。
3、两种注册方式的区别和优缺点:
动态注册:生命周期与程序的生命周期一致,程序关闭后将接收不到广播。
静态注册:即使程序关闭也能接收到广播。
4、两种注册方式的优缺点:
动态注册:
(1) 优点:优先级高于静态注册,优先收到广播。
(2) 缺点:注册广播的Activity关闭,广播也将无法接收。
静态注册:
(1) 优点:无需担心广播接收器是否被关闭,应用若未运行,将会被唤醒并接收广播。
(2) 缺点:优先级较低。
二、接收器匹配策略
1、Broadcast发送主流程
Broadcast发送的主流程为先匹配action,然后匹配data,最后匹配category。
这里不再介绍广播接收器的注册源码流程,也不介绍广播的发送源码流程,请参看老罗的文章:Android应用程序注册广播接收器(registerReceiver)的过程分析和Android应用程序发送广播(sendBroadcast)的过程分析,这里只介绍广播的匹配策略。代码在android/content/IntentFilter.java中,match()方法的大致流程如下:
public final int match(String action, String type, String scheme,
Uri data, Set<String>categories, String logTag) {
if (action != null && !matchAction(action)) {//没匹配action
returnNO_MATCH_ACTION; // -3:due todifferent actions
}
int dataMatch = matchData(type, scheme, data);
if (dataMatch < 0) {//没匹配type或data
return dataMatch;// -1:due to different MINE types, -2:due to different data URIs
}
String categoryMismatch = matchCategories(categories);
if (categoryMismatch != null) {//没匹配category
return NO_MATCH_CATEGORY;//-4:because it required one or more categories
}
return dataMatch;
}
该方法用于匹配intent数据,共有4项:action、type、data和category,任何一项匹配不成功都会失败,即BroadcastReceiver收不到广播。
1、匹配子流程
(1) match action
这个过程比较简单,只是检查一下action是否为null,以及ArrayList<String>列表mActions是否包含action。
public final boolean matchAction(String action) {
return hasAction(action);
}
public final boolean hasAction(String action) {
return action != null && mActions.contains(action);
}
(2) match data和type
若action匹配,则检查data。这个流程比较复杂,出口也比较多。
public final int matchData(String type, String scheme, Uri data) {
final ArrayList<String> types = mDataTypes;
final ArrayList<String> schemes = mDataSchemes;
int match = MATCH_CATEGORY_empty;
//若IntentFilter的mDataTypes、mDataSchemes为空,且intent的type、data也为空,则匹配
if (types == null && schemes == null) {
return ((type == null&& data == null)
? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL): NO_MATCH_DATA);
}
//若IntentFilter的mDataSchemes不为空,则检查intent的scheme
if (schemes != null) {
if (schemes.contains(scheme != null? scheme : "")) {
match =MATCH_CATEGORY_SCHEME;
} else {
return NO_MATCH_DATA;
}
final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
if (schemeSpecificParts != null && data != null) {
// Uri格式:[scheme:]scheme-specific-part[#fragment],即scheme、scheme-specific-part、//fragment三部分
//进一步划分是[scheme:][//authority][path][?query][#fragment]
//再进一步划分[scheme:][//host:port][path][?query][#fragment]
match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart())
? MATCH_CATEGORY_SCHEME_SPECIFIC_PART :NO_MATCH_DATA;
}
if (match !=MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
// If there isn't anymatching ssp, we need to match an authority.
final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
if (authorities != null) {//匹配Authority,即匹配host:port部分
int authMatch =matchDataAuthority(data);
if (authMatch >= 0){//匹配Authority,则继续检查path
finalArrayList<PatternMatcher> paths = mDataPaths;
if (paths == null) {
match = authMatch;
} else if(hasDataPath(data.getPath())) {
match =MATCH_CATEGORY_PATH;
} else {
return NO_MATCH_DATA;
}
} else {
return NO_MATCH_DATA;// 不匹配Authority
}
}
}
// If neither an ssp nor an authority matched, we're done.
if (match == NO_MATCH_DATA) {
return NO_MATCH_DATA;
}
} else {
// Special case: match either an Intent with no data URI,
// or with a scheme: URI. This is to give aconvenience for
// the common case where you want to deal with data in a
// content provider, which is done by type, and we don't want
// to force everyone to say they handle content: or file: URIs.
// 若scheme不是""或"content"或"file" 就不匹配;否则继续后续判断
if (scheme != null && !"".equals(scheme)
&& !"content".equals(scheme)
&& !"file".equals(scheme)){
return NO_MATCH_DATA;
}
}
if (types != null) {//filter中适配了types
if (findMimeType(type)) {//检查intent的type是否满足mDataTypes的要求
match =MATCH_CATEGORY_TYPE;
} else {
return NO_MATCH_TYPE;
}
} else {
// If no MIME types are specified,then we will only match against
// an Intentthat does not have a MIME type.
if (type != null) {//filter要匹配types,但intent没携带type,则不匹配
return NO_MATCH_TYPE;
}
}
return match + MATCH_ADJUSTMENT_NORMAL;
}
(3) match category
这个过程较为简单,主要检查了3个场景:
1) 若intent的category为空,则匹配成功;
2) 若filter的category为空,且intent中没有category,则匹配成功;
3) 若filter的category不为空,同时intent中也有category,并且都包含在filter的category中,即intent中的category都包含在filter的category中,则匹配成功;
/**
* Match this filter againstan Intent's categories. Each category in
* the Intent must bespecified by the filter; if any are not in the
* filter, the match fails.
*
* @paramcategories The categories included in the intent, as returned by
* Intent.getCategories()*
* @return If all categories match(success), null; else the name of the
* first category that didn't match.
*/public final String matchCategories(Set<String>categories) {
if (categories == null) {//intent的categories为空,则匹配成功
return null;
}
Iterator<String> it = categories.iterator();
//filter中的Categories为空,且intent中没有Categories,则匹配成功
if (mCategories == null) {
return it.hasNext() ?it.next() : null;
}
while (it.hasNext()) {
//filter有Categories, 且包含所有的intent里Categories才算成功,否则失败
final String category =it.next();
if (!mCategories.contains(category)){
return category;
}
}
return null;
}
但是,从代码中看,却存在第4中场景,即若filter的mCategories不为空,且intent中的categories也不为空,但大小却为0,也能够匹配成功。
我们先从源码android/content/Intent.java中addCategory()方法查起:
public IntentaddCategory(String category) {
if (mCategories == null) {
mCategories = newArraySet<String>();
}
mCategories.add(category.intern());
return this;
}
过程很简单,把category添加到mCategories中,即执行了addCategory()方法之后,Intent中的mCategories就不为空了,且大小>0。
若想让intent中的mCategories不为空,且大小变为0,可以试想删除mCategories中的数据会发生什么情况,即执行android/content/Intent.java中removeCategory()方法:
public voidremoveCategory(String category) {
if (mCategories != null) {
mCategories.remove(category);
if (mCategories.size() == 0) {
mCategories = null;
}
}
这次就明白了,当mCategories的大小变为0后,它就被赋值为空了,因此intent中的categories要么为空,要么它的大小一定大于0。
/**
* Return the set of all categoriesin the intent. If there are no categories,
* returnsNULL.
*/public Set<String>getCategories() {
return mCategories;
}
所以,不存在第4中场景。
相关阅读
在前面的章节我们介绍到了 Activity、Service、BroadCastReceiver,这三者的启动、数据的传递都用到了 Intent,足以可见 Intent 在 A