Android Intent 匹配规则简析

来源:互联网 发布:python urllib2 json 编辑:程序博客网 时间:2024/05/29 07:09

转载请标明出处:http://blog.csdn.net/edisonchang/article/details/50439610

对于Intent,接触过Android应用程序开发的朋友一定再熟悉不过了。Intent字面的解释“意图”也很好的诠释了设计思路,在应用开发中,Intent频繁用于Activtiy,Service和BroadcastReceiver三大组件的启动。关于Intent的机制和匹配规则,感兴趣的朋友可以参考这两篇博客,android Intent机制详解,Intent匹配规则以及解析框架深入分析,本篇文章主要针对Intent匹配过程中所涉及的源码进行简要分析。

这里有两个非常重要的类要介绍,一个是Intent,一个是IntentFilter。IntentFilter 用来向系统注册一些过滤条件,除Contentprovider 外的三大android 系统组件都需要通过IntentFilter 向系统注册,至于注册方式还可以分为静态和动态两种,此处就不再详细展开。源码位于:Android\sdk\sources\android-22\android\content\IntentFilter.java,

public class IntentFilter implements Parcelable {    private final ArrayList<String> mActions;    private ArrayList<String> mCategories = null;    private ArrayList<String> mDataSchemes = null;    private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;    private ArrayList<AuthorityEntry> mDataAuthorities = null;    private ArrayList<PatternMatcher> mDataPaths = null;    private ArrayList<String> mDataTypes = null;

Intent提供启动组件所需满足的条件,IntentFilter在前预先占坑,根据Intent提供的信息匹配上对应的组件。 源码位于:Android\sdk\sources\android-22\android\content\Intent.java ,

public class Intent implements Parcelable, Cloneable { private String mAction;    private Uri mData;    private String mType;    private String mPackage;    private ComponentName mComponent;    private int mFlags;    private ArraySet<String> mCategories;    private Bundle mExtras;

从属性的声明中不难看出,Intent中属性类型基本上都是单个类型的,而IntentFilter
属性都是数组集合类型的。Intent代表的是特定规则,IntentFilter是承载规则的容器。

IntentFilter中的match()方法是处理匹配的关键,从源码(如下)组成结构也不难看出匹配有matchAction、 matchData和matchCategories三个过程,所以匹配规则包括Action、Data、Category 三类。

 public final int match(String action, String type, String scheme,            Uri data, Set<String> categories, String logTag) {        if (action != null && !matchAction(action)) {            if (false) Log.v(                logTag, "No matching action " + action + " for " + this);            return NO_MATCH_ACTION;        }        int dataMatch = matchData(type, scheme, data);        if (dataMatch < 0) {            if (false) {                if (dataMatch == NO_MATCH_TYPE) {                    Log.v(logTag, "No matching type " + type                          + " for " + this);                }                if (dataMatch == NO_MATCH_DATA) {                    Log.v(logTag, "No matching scheme/path " + data                          + " for " + this);                }            }            return dataMatch;        }        String categoryMismatch = matchCategories(categories);        if (categoryMismatch != null) {            if (false) {                Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);            }            return NO_MATCH_CATEGORY;        }        // It would be nice to treat container activities as more        // important than ones that can be embedded, but this is not the way...        if (false) {            if (categories != null) {                dataMatch -= mCategories.size() - categories.size();            }        }        return dataMatch;    }

Action和Category的匹配规则相对简单,进行匹配时Intent携带的Action字段值和Category字段值必须包含在IntentFilter中,否则匹配失败。

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) {            if (schemes.contains(scheme != null ? scheme : "")) {                match = MATCH_CATEGORY_SCHEME;            } else {                return NO_MATCH_DATA;            }            final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;            if (schemeSpecificParts != 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);                    if (authMatch >= 0) {                        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 {            // 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) {            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;    }

以上是matchData的源码,sdk说明如下,

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.

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).

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.

按照sdk的意思只有以下几种情况Data会匹配成功
(1)如果Intent过滤器没有指定MIME type 和Uri,Intent必须不包括Mime Type 和Uri才能匹配成功;
(2)Intent过滤器不指定schemes, Intent必须没有包括scheme 或者scheme是file或者content才能匹配成功;
(3)Intent不指定MIME type, Intent必须也不能指定MIME type才能匹配成功;
(4)Intent对象既包含Uir,又指定MIME type ,只有在MIME type与过滤器中的MIME type之一匹配,且Uri同样存在过滤器中时,才能匹配成功;

需要注意的是,MIME type, Uri scheme, host name都是区分大小写的,所以进行匹配时一定要注意 。

好了,到这里基本就介绍完了,有任何问题,欢迎补充指正。

0 0