必威体育Betway必威体育官网
当前位置:首页 > IT技术

Broadcast的Intentfilter过滤策略

时间:2019-10-10 03:45:46来源:IT技术作者:seo实验室小编阅读:56次「手机版」
 

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中场景。

相关阅读

Intent 与 IntentFilter 详解

在前面的章节我们介绍到了 Activity、Service、BroadCastReceiver,这三者的启动、数据的传递都用到了 Intent,足以可见 Intent 在 A

分享到:

栏目导航

推荐阅读

热门阅读