Broadcast的Intentfilter过滤策略

xiaoxiao2021-02-28  66

一、注册方式

    作为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) {//没匹配typedata

        return dataMatch;// -1due to different MINE types, -2due to different data URIs

    }

 

    String categoryMismatch = matchCategories(categories);

    if (categoryMismatch != null) {//没匹配category

        return NO_MATCH_CATEGORY;//-4because 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;

    //IntentFiltermDataTypesmDataSchemes为空,且intenttypedata也为空,则匹配

    if (types == null && schemes == null) {

        return ((type == null&& data == null)

                ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL): NO_MATCH_DATA);

    }

    //IntentFiltermDataSchemes不为空,则检查intentscheme

    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]schemescheme-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)) {//检查intenttype是否满足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) {//intentcategories为空,则匹配成功

        return null;

    }

 

    Iterator<String> it = categories.iterator();

    //filter中的Categories为空,且intent中没有Categories,则匹配成功

    if (mCategories == null) {

        return it.hasNext() ?it.next() : null;

    }

 

    while (it.hasNext()) {

    //filterCategories, 且包含所有的intentCategories才算成功,否则失败

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

转载请注明原文地址: https://www.6miu.com/read-1950257.html

最新回复(0)