Intents and Intent Filters

本文主要分享关于Intents和Intent Filters的匹配的一些知识。
参考资源:
http://developer.android.com/guide/components/intents-filters.html

Android 4.2源码 IntentFilter.java


一、概述

Intent可以用于启动Android四大组件中的三个(Activity、Service和Receiver,这里不包含Provider)。
启动的方法包括:
启动Activity:Context.startActivity()和Activity.startActivityForResult()
启动Srevice:Context.startService() 和Context.bindService()
启动Receiver:Context.sendBroadcast()、Context.sendOrderedBroadcast()和Context.sendStickyBroadcast()

启动的方式有两种:显示启动和隐式启动。
显式启动:在Intent中指明了ComponentName,关于这部分没有什么可讨论的,直接就会启动对应的组件。
隐式启动:在Intent中没有指明ComponentName,这部分则是我们要讨论的重点。每个组件都需要在AndroidManifest.xml文件中声明,在声明的时候会指明一些Intent Filter,当某个Intent能够和其中某个Intent Filter匹配时就会启动这个组件。类似这样的声明: <intent-filter>
有个例外,broadcast receivers可以不在AndroidManifest.xml中声明,而在代码中通过Context.registerReceiver()去注册,Activity和Service必须在AndroidManifest.xml文件中声明。

一个组件可能含有零个、一个或多个Intent Filter。
如果含有零个,则只能通过显示地启动这个组件;
如果含有一个,则只要Intent能通过这个Intent Filter的检查,就能启动这个组件;
如果含有多个,则只要Intent能通过任意一个Intent Filter的检查,就能启动这个组件。

接下来将分析Intent Filter对Intent的匹配检查。

二、匹配检查

匹配检查分为三步:
1.Action test;
2.Category test;
3.Data test.
三步检查都通过才能够算是成功,否则即为失败。
注:我们知道一个Intent还包括Extras、Flags等信息,这些信息在匹配的过程中会被忽略,匹配过程不需要这些信息。因为Intent Filter中并没有这些信息,因此不会去检查。

1、Action test

对于Intent,它至多包含一个Action,可以没有Action;
对于Intent Filter,则可以包含一系列Action,当然也可以没有Action。

如果Intent中不包含Action,则这项test直接通过;
如果Intent Filter不包含任何Action,则所有的Intent都不能通过这项test;
如果Intent Filter包含Intent的Action,则通过test。

有个疑问,如果Intent和Intent Filter都没有Action,则是通过还是Fail呢?
两种解释,似乎有些矛盾:

解释一:来自Android API--http://developer.android.com/guide/components/intents-filters.html
If the filter fails to list any actions, there is nothing for an intent to match, so all intents fail the test. No intents can get through the filter.
On the other hand, an Intent object that doesn't specify an action automatically passes the test — as long as the filter contains at least one action.
意思是:只要Intent Filter包含至少一个Action,则不包含Action的Intent就可以通过;如果都不包含Action,则会Fail。

解释二:Android 4.2源码 MTK 6572平台 IntentFilter.java

public final int match(String action, String type, String scheme,
            Uri data, Set<String> categories, String logTag) {
        //这里只有在以下情形下才会return NO_MATCH_ACTION
        //Intent的action不为空,且Intent Filter中不包含这个action
        //换言之,如果Intent的action为空,则根本不做任何检查,那就是通过了Action test
        if (action != null && !matchAction(action)) {
            if (false) Log.v(
                logTag, "No matching action " + action + " for " + this);
            return NO_MATCH_ACTION;
        }
        ……
}

个人倾向于解释二。

相关源码
public final boolean matchAction(String action) {
    return hasAction(action);
}
public final boolean hasAction(String action) {
    //private final ArrayList<String> mActions;
    //这里似乎action为空则返回false,但是在action为空时match方法根本不会调用这个方法去检查
    return action != null && mActions.contains(action);
}


2、Category test

对于Intent,它可以包含零个、一个或多个Category;Intent Filter也是如此。
只有Intent中所有的Category都能在Intent Filter中能找到,才能通过这个test;
如果Intent中没有Category,则不过Intent Filter中是否有Category,都直接通过test。关于这一点,不像Action似乎有争议(有争议是指本人没弄明白)。

有如下特例:
Android treats all implicit intents passed to startActivity() as if they contained at least one category: "android.intent.category.DEFAULT".
Therefore, activities that are willing to receive implicit intents must include "android.intent.category.DEFAULT" in their intent filters.
(Filters with "android.intent.action.MAIN" and "android.intent.category.LAUNCHER" settings are the exception. They mark activities that begin new tasks and that are represented on the launcher screen. They can include "android.intent.category.DEFAULT" in the list of categories, but don't need to.)  

源码如下:
//return null则表明通过了Category test
public final String matchCategories(Set<String> categories) {
    //如果Intent中没有category,那么直接就通过了Category test
    if (categories == null) {
        return null;
    }

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

    //如果Intent中有Category,而Filter中一个Category也没有,那么就不能通过Category test
    if (mCategories == null) {
        return it.hasNext() ? it.next() : null;
    }

    //如果Intent中有Filter中不存在的Category,那么test fail
    while (it.hasNext()) {
        final String category = it.next();
        if (!mCategories.contains(category)) {
            return category;
        }
    }

    return null;
}


3、Data test

这个test相比前两个更为复杂一些。
一个Intent Filter可以包含零个、一个或多个Data。
一个Data可包含两部分:一、data type;二、URI。而URI又可以包含四个部分:scheme://host:port/path
host和port共同构成一个authority。
这四个部分都是可选的,但彼此却是互相关联的:
如果没有定义scheme,那么authority也是无意义的;
如果没有host,那么port也没有意义;
如果没有定义scheme,那么authority也是无意义的;
如果没有定义scheme和authority,那么path也是无意义的。
关于这部分,可以粗略地认为这四个部分要想某一部分有意义,那它之前的部分必须存在。

这里有句话很重要:The data test compares both the URI and the data type in the Intent object to a URI and data type specified in the filter.
它的意思应该是在data test的过程中,以filter为主。

一个Data实例可能有以下四个case:
1.既没有data type,也没有URI;
如果Intent是这种case,那么只有Intent Filter应该也是这种case,那才能通过这个测试。
如果最终能够匹配成功,那么会返回MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL。

2.有data type,但没有URI;
如果Intent是这种case,那么Intent Filter应该也是这种case,而且Intent的data type能与Filter的data type匹配。
data type的匹配本文未涉及。

3.没有data type,但有URI;
如果Intent是这种case,那么Intent Filter应该也是这种case,而且Intent的URI能与某个Filter的URI匹配。
后面再来分析URI的匹配,这里先跳过。

4.既有data type,又有URI。
如果Intent是这种case,那么满足以下两个条件就能匹配:
(1)Intent的data type能与Filter的data type匹配
(2)Intent的URI能与某个Filter的URI匹配,或者Filter没有URI,而Intent的URI的sheme是content或者file。
In other words, a component is presumed to support content: and file: data if its filter lists only a data type.

三、部分源码分析

下面来看match的源码(重点看matchData的test)

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

    //Secondly, Data test
    int dataMatch = matchData(type, scheme, data);
    if (dataMatch < 0) {
        //Data test fail
        return dataMatch;
     }    
     
     //Finally, Category test 
     String categoryMismatch = matchCategories(categories); 
     if (categoryMismatch != null) {
        //Category test fail
        return NO_MATCH_CATEGORY;
    } 

     return dataMatch;
}

其中的matchAction和matchCategories两个方法前面已经给出了,这里只看最复杂的matchData方法

public final int matchData(String type, String scheme, Uri data) {
    final ArrayList<String> types = mDataTypes;
    final ArrayList<String> schemes = mDataSchemes;
    final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
    final ArrayList<PatternMatcher> paths = mDataPaths;

    int match = MATCH_CATEGORY_EMPTY;

    //case 1:Intent Filter has neither types nor URI(a meaningful URI must has scheme)
    if (types == null && schemes == null) {
	//if Intent has no type and no scheme then it will pass this test
        return ((type == null && data == null)
            ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
    }

    //case 2:Intent Filter has URI(schemes)
    if (schemes != null) {
	//firstly, Intent must has scheme which can be found in Intent Filter's schemes
       //这里如果能够成功,此时匹配成功的级别是MATCH_CATEGORY_SCHEME
       if (schemes.contains(scheme != null ? scheme : "")) {
            match = MATCH_CATEGORY_SCHEME;
        } else {
            return NO_MATCH_DATA;
        }

	//secondly, 通过了scheme test后,如果Filter有authority,那么就进入到authority test
	//如果Intent Filter没有authority,那么这里的authority测试就直接pass了
        if (authorities != null) {
	    //Intent Filter has authority, so you must match Data Authority
            int authMatch = matchDataAuthority(data);
            if (authMatch >= 0) {
		//现在你通过了authority test,接下来看是否需要进入到path test
                if (paths == null) {
                    match = authMatch;
                } else if (hasDataPath(data.getPath())) {
                //path test通过,匹配成功的级别升级为MATCH_CATEGORY_PATH
                    match = MATCH_CATEGORY_PATH;
                } else {
                    return NO_MATCH_DATA;
                }
            } else {
                return NO_MATCH_DATA;
            }
        }
    } else {
        //如果Filter没有scheme,那么Intent若也没有scheme,则pass;若有,则必须为空,或content、或file,否则fail test
        if (scheme != null && !"".equals(scheme)
                && !"content".equals(scheme)
                && !"file".equals(scheme)) {
            return NO_MATCH_DATA;
        }
    }

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

    return match + MATCH_ADJUSTMENT_NORMAL;
}


你可能感兴趣的:(Intents and Intent Filters)