Intent Filter match過程源碼分析

主流程

  • 主線流程:先match action, 再match data, 最後match category
  • 時序圖match
  • 簡化後的代碼

    public final int match(String action, String type, String scheme,
            Uri data, Set<String> categories, String logTag) {
        if (action != null && !matchAction(action)) {
            return NO_MATCH_ACTION;
        }
    
        int dataMatch = matchData(type, scheme, data);
        if (dataMatch < 0) {
            return dataMatch;
        }
    
        String categoryMismatch = matchCategories(categories);
        if (categoryMismatch != null) {
            return NO_MATCH_CATEGORY;
        }
    
        return dataMatch;
    }

子流程

match action

  • 這個子流程很簡單,只判斷action是否包含在intent filter
  • 代碼

    /**
     * Match this filter against an Intent's action.  If the filter does not
     * specify any actions, the match will always fail.
     *
     * @param action The desired action to look for.
     *
     * @return True if the action is listed in the filter.
     */
    public final boolean matchAction(String action) {
        return hasAction(action);
    }
    
    public final boolean hasAction(String action) {
        return action != null && mActions.contains(action);
    }

match data

  • 這個子流程稍顯複雜,代碼出口太多,結合uri的語法組成來看[scheme:]scheme-specific-part[#fragment] / [scheme:][//authority][path][?query][#fragment]
  • 代碼

    
    /**
     * Match this filter against an Intent's data (type, scheme and path). If
     * the filter does not specify any types and does not specify any
     * schemes/paths, the match will only succeed if the intent does not
     * also specify a type or data.  If the filter does not specify any schemes,
     * it will implicitly match intents with no scheme, or the schemes "content:"
     * or "file:" (basically performing a MIME-type only match).  If the filter
     * does not specify any MIME types, the Intent also must not specify a MIME
     * type.
     *
     * <p>Be aware that to match against an authority, you must also specify a base
     * scheme the authority is in.  To match against a data path, both a scheme
     * and authority must be specified.  If the filter does not specify any
     * types or schemes that it matches against, it is considered to be empty
     * (any authority or data path given is ignored, as if it were empty as
     * well).
     *
     * <p><em>Note: MIME type, Uri scheme, and host name matching in the
     * Android framework is case-sensitive, unlike the formal RFC definitions.
     * As a result, you should always write these elements with lower case letters,
     * and normalize any MIME types or Uris you receive from
     * outside of Android to ensure these elements are lower case before
     * supplying them here.</em></p>
     *
     * @param type The desired data type to look for, as returned by
     *             Intent.resolveType().
     * @param scheme The desired data scheme to look for, as returned by
     *               Intent.getScheme().
     * @param data The full data string to match against, as supplied in
     *             Intent.data.
     *
     * @return Returns either a valid match constant (a combination of
     * {@link #MATCH_CATEGORY_MASK} and {@link #MATCH_ADJUSTMENT_MASK}),
     * or one of the error codes {@link #NO_MATCH_TYPE} if the type didn't match
     * or {@link #NO_MATCH_DATA} if the scheme/path didn't match.
     *
     * @see #match
     */
    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;
    
        //1. 若filter的type scheme爲空,且intent的type data也爲空則匹配
        //   否則不匹配
        if (types == null && schemes == null) {
            return ((type == null && data == null)
                ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
        }
    
        if (schemes != null) {
        //2. 若filter的schemes不爲空且intent的scheme包含在裏面則進行後續判斷;
        //   否則不匹配
            if (schemes.contains(scheme != null ? scheme : "")) {
                match = MATCH_CATEGORY_SCHEME;
            } else {
                return NO_MATCH_DATA;
            }
    
            final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
            if (schemeSpecificParts != null) {
            //3. 若schemeSpecificParts不爲空,則用data的SchemeSpecificPart來做匹配
            //   並修改match的值。uri語法:[scheme:]scheme-specific-part[#fragment] / [scheme:][//authority][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 any matching ssp, we need to match an authority.
                final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
                // 4. 若schemeSpecificPart(ssp)不匹配,則要匹配authority
                // 也就是判斷host port部分
                if (authorities != null) {
                    int authMatch = matchDataAuthority(data);
                    if (authMatch >= 0) {
                    // 5. host port匹配,則繼續進行path的匹配
                        final ArrayList<PatternMatcher> paths = mDataPaths;
                        if (paths == null) {
                            match = authMatch;
                        } else if (hasDataPath(data.getPath())) {
                            match = MATCH_CATEGORY_PATH;
                        } else {
                            return NO_MATCH_DATA;
                        }
                    } else {
                    // host port不匹配
                        return NO_MATCH_DATA;
                    }
                }
            }
            // If neither an ssp nor an authority matched, we're done.
            if (match == NO_MATCH_DATA) {
                return NO_MATCH_DATA;
            }
        } else {
        // filter的schemeSpecificParts不爲空且和intent的SchemeSpecificPart匹配時,
        // 若scheme不是"" "content" "file" 就不匹配;否則繼續後續判斷
            // Special case: match either an Intent with no data URI,
            // or with a scheme: URI.  This is to give a convenience 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.
            if (scheme != null && !"".equals(scheme)
                    && !"content".equals(scheme)
                    && !"file".equals(scheme)) {
                return NO_MATCH_DATA;
            }
        }
    
        if (types != null) {
        // 6. type即MIME
        // 若type不爲空則判斷filter和intent的type是否包容,否則不匹配
            if (findMimeType(type)) {
                match = MATCH_CATEGORY_TYPE;
            } else {
                return NO_MATCH_TYPE;
            }
        } else {
            // If no MIME types are specified, then we will only match against
            // an Intent that does not have a MIME type.
            if (type != null) {
        // 若filter的types集合是空,但intent的不是空,不匹配
                return NO_MATCH_TYPE;
            }
        }
    
        return match + MATCH_ADJUSTMENT_NORMAL;
    }

match category

  • 這個子流程也不復雜,只判斷intent的category是否完全包含在filter裏
  • 代碼

    /**
     * Match this filter against an Intent's categories.  Each category in
     * the Intent must be specified by the filter; if any are not in the
     * filter, the match fails.
     *
     * @param categories 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) {
            return null;//匹配成功
        }
    
        Iterator<String> it = categories.iterator();
    
        if (mCategories == null) {//filter沒有Categories;intent有則失敗,無則成功
            return it.hasNext() ? it.next() : null;
        }
    
        while (it.hasNext()) {//filter有Categories, intent的Categories要全部包含在filter裏纔算成功
            final String category = it.next();
            if (!mCategories.contains(category)) {
                return category;
            }
        }
    
        return null;
    }

小結

  • 匹配過程總體分三步走,match action-> match data-> match categories,任意一步失敗就不在進行後續處理。
  • match action就是匹配intent的action是否包含在filter的actions裏。
  • match data的思路和uri的語法組成有關[scheme:]scheme-specific-part[#fragment]
    • 先判filter和intent的types(即MIMI) schemes是否全是null,若是則匹配,取值MATCH_CATEGORY_EMPTY,否則繼續
    • 再匹配scheme,不匹配則結束,匹配則繼續
    • SchemeSpecificPart部分,若匹配,則進行最後的type(MIME)判斷
    • SchemeSpecificPart部分,若不匹配,則
      • 匹配host port部分,成功則繼續,否則不匹配
      • 匹配path部分,成功則繼續,否則不匹配
    • 最後進行type(MIME)的匹配
  • match categories就是判斷intent的所有category是否全都包含啊filter的categories裏。
  • uri的語法組成[scheme:]scheme-specific-part[#fragment] / [scheme:][//authority][path][?query][#fragment]

參考

發佈了113 篇原創文章 · 獲贊 6 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章