Broadcast的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) {//沒匹配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中場景。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章