IntentFilter过滤规则

1,389 阅读3分钟

    我们启动Activity时可以使用显式调用和隐式调用,显式调用需要制定要启动组件的信息,例如包名和类名,而隐式调用则通过IntentFilter进行匹配,下面则记录一下IntentFiler的过滤规则

一、action

  1. action是一个字符串,Intent中的action必须与<intent-fileter>中的任何一个action完全相同
  2. action区分大小写,大小写不同则匹配失败


二、category

  1. Intent中可以有多个category,Intenter中每一个category都必须与<intent-fileter>任何一个匹配
  2. 如果Intent没有添加category,系统调用startActivity或者startActivityForResult会默认带上android.intent.category.DEFAULT
  3. 需要隐式启动的Activity必须在<intent-fileter>中加android.intent.category.DEFAULT标签,原因如2


三、data

  1. Activity若含有data标签,则Intent必须要有与之匹配的data
  2. data的语法:

    <data android:scheme="string"
              android:host="string"
              android:port="string"
              android:path="string"
              android:pathPattern="string"
              android:pathPrefix="string"
              android:mimeType="string"/>

    Scheme:URI的模式。
    Host:URI主机名
    Port:URI端口号
    Path、pathPattern、pathPrefix:表述路径信息,path标识完整的路径信息;pathPattern 也表示完整路径信息,但是它里面可以包含通配符"*"表示0个或多个任意字符;pathPrefix表示路径的前缀信息。
    mimeType:指媒体类型,如"image/*","audio/*","video/*",data标签可以只有android:mimeType。
  3. 如果没有指定URI而指定了mimeType,则Intent中的URI默认是content或者file才能匹配。


四、源码解析

    下面来看下源码是如何对这些进行解析过滤的,过滤的逻辑在IntentFilter.java中

public final int match(String action, String type, String scheme,
        Uri data, Set<String> categories, String logTag) {
    if (action != null && !matchAction(action)) {
        // 过滤action
        return NO_MATCH_ACTION;
    }

    int dataMatch = matchData(type, scheme, data);
    if (dataMatch < 0) {
        // 过滤data
        return dataMatch;
    }

    String categoryMismatch = matchCategories(categories);
    if (categoryMismatch != null) {
        // 过滤category
        return NO_MATCH_CATEGORY;
    }
    return dataMatch;
}

    从这里可以看到,IntentFilter会先匹配<action>然后匹配<data>,最后才会匹配<category>

<action>匹配规则

public final boolean matchAction(String action) {
    return hasAction(action);
}

public final boolean hasAction(String action) {
    return action != null && mActions.contains(action);
}

    从代码中可以看出,一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(注意,区分大小写,大小写不同但字符串内容相同也会造成匹配失败),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;

    if (types == null && schemes == null) {
        return ((type == null && data == null)
            ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
    }

    if (schemes != null) {
        // 先进行schemes匹配,失败则返回
        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) {
            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;
            if (authorities != null) {
                int authMatch = matchDataAuthority(data); // host匹配
                if (authMatch >= 0) {
                    // 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 {
                    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 {
        if (scheme != null && !"".equals(scheme)
                && !"content".equals(scheme)
                && !"file".equals(scheme)) {
            return NO_MATCH_DATA;
        }
    }

    if (types != null) {
        // 进行mimeType匹配
        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) {
            return NO_MATCH_TYPE;
        }
    }

    return match + MATCH_ADJUSTMENT_NORMAL;
}

    从代码中可以看到,<data>的匹配顺序如下:

  1. 匹配schemes
  2. 匹配host
  3. 匹配path
  4. 匹配mimeType

<category>匹配规则

public final String matchCategories(Set<String> categories) {
    if (categories == null) {
        return null;
    }

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

    if (mCategories == null) {
        return it.hasNext() ? it.next() : null;
    }

    while (it.hasNext()) {
        // 对Intent中每一个category都要进行比对
        final String category = it.next();
        if (!mCategories.contains(category)) {
            return category;
        }
    }

    return null;
}

    从这里可以看到,Intent中的所有category都必须进行匹配成功就可以

参考资料

Android InterFilter的匹配规则

IntentFilter 匹配规则,源码解析

《Android开发艺术探索》