Intent Filter match过程源码分析

主流程

  • 主线流程:先match action, 再match data, 最后match category
  • 时序图《Intent Filter 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]

参考

    原文作者:cheyiliu
    原文地址: https://blog.csdn.net/cheyiliu/article/details/47323065
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞