隐式Intent 的匹配规则

0x00 几个问题

注:这里的“调用者”是指调用startActivitystartService的App。

  1. 调用者的Intent中如果不指定Action,能否与目标Activity匹配上?
    其中(1)目标Activity的IntentFilter中指定了Action,(2)其他条件都匹配。

  2. 如果通过Action匹配一个Service?调用者要启动Service,调用者的Intent中指定了Action,但是指定目标Service的类名。

  3. 调用者的Intent中如果不指定Category,能否与目标Activity匹配上?
    其中(1)目标Activity的IntentFilter中指定了多个Category,(2)其他条件都匹配。

如果你不确定答案,就往下看看吧:)

0x01 两类Intent:显式Intent和隐式Intent

显式(Explicit)Intent,是指在Intent中指明了要启动的包名packageName和类名className。在代码中表现为调用了intent.setComponent()或者intent.setClassName()

隐式(Implicit)Intent,是指在Intent中不指明 packageName和className,而是指定了Action,Data,Category

Intent匹配用在Activity、BroadcastReceiver和Service中。

Android 5.0 (LL)之后,不可以采用隐式Intent来指定Service,因为不安全。安全的做法是,必须指定packageName或者packageName和className。这在Android代码中强制保证了。
参考ContextImpl.java中的validateServiceIntent()startService()时会调用validateServiceIntent()以检查Intent是否合法。

    private void validateServiceIntent(Intent service) {
        if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {// 5.0(含)之后就会抛异常,LOLLIPOP值为21
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);
                throw ex;
            } else {// 如果targetSdkVersion是20或更小,则仅仅提示不安全
                Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
            }
        }
    }

从上面代码中可以看到,可以通过以下Intent启动Service:
方式一:Intent中指定包名、类名,即调用intent.setComponent()或者intent.setClassName()设置package和class。

方式二: Intent中指定包名和Action,即调用intent.setPackage()intent.setAction()。Action也可以在构造Intent时传入,即new Intent(Action) 。

0x02 隐式Intent的匹配规则

(1) 谁跟谁匹配?启动Activity、Receiver、Service的Intent和AndroidManifest.xml中声明的组件信息(目标组件)进行比较。例如Action的匹配mActions.contains(action),其中mActions为AndroidManifest.xml中声明的某个组件所接受的所有Actions。实参action为Intent中的Action。

(2) 只匹配Action,Data,Category,不匹配其他值,例如mExtras中的数据,mFlags。

(3) 匹配顺序为:Action -> Data -> Category。即先检查Action是否匹配,再检查Data,最后检查Category,只有这3项都匹配了,才算Intent匹配上。否则Intent不匹配。

对于Action、Data和Category中的某一项的匹配规则如下,再次强调,Intent是否匹配要看Action、Data和Category是否都匹配上。

Action的匹配规则

匹配项发送的Intent组件声明(AndroidManifest.xml)结果
Action包含包含Intent中的Action要匹配组件声明时其中一个Action,才算Action匹配;否则Action不匹配。代码为mActions.contains(action)
Action包含不包含Action不匹配
Action不包含包含(或不包含)Action匹配,注意Intent中不对Action赋值,是可以匹配通过的。

Action匹配通过,才会进一步匹配Data。

Data的匹配规则

匹配项发送的Intent组件声明(AndroidManifest.xml)结果
Data既不包含Uri,也不包含MIME type不包含任何Uri和MIME type;Data匹配
Data既不包含Uri,也不包含MIME type包含Uri或MIME;Data不匹配
Data含Uri,无MIME含Uri并匹配,且无MIMEData匹配
Data含Uri,无MIME其他情况(即除了“含Uri并匹配,且无MIME”这种情况)Data不匹配
Data含MIME,无Uri含MIME并匹配,且无UriData匹配
Data含MIME,无Uri其他情况(即除了“含MIME并匹配,且无Uri”这种情况)Data不匹配
Data含Uri,含MIME含MIME并匹配且含Uri并匹配Data匹配
Data含Uri,含MIME含MIME并匹配,并且不含Uri如果Intent中的Uri是content:或者file:类型的Uri,则Data匹配,否则Data不匹配


其中Uri的匹配规则如下:
Uri:<scheme>://<authority>/<path>
(1) 如果intent filter中仅指定了scheme,则(Intent中)所有包含该scheme的Uri都匹配;
(2) 如果filter中指定了scheme,authority,无path,则(Intent中)所有相同scheme和authority的Uri都匹配。path忽略;
(3) 如果filter中同时指定了scheme,authority和path,则(Intent中)完全一样才能匹配。

Data 匹配通过,才会进一步匹配Category 。

Category的匹配规则

匹配项发送的Intent组件声明(AndroidManifest.xml)结果
Category包含包含Intent中每一个category都要与AndroidManifest.xml中某组件声明的所有Categories中某项匹配,则Category匹配;否则,Category不匹配。代码见后面。
Category包含不包含Category不匹配
Category不包含包含(或不包含)Category匹配

Category也匹配通过了,那么Intent就匹配上了。

0x03 附Intent匹配的代码

    // match()的参数是传过来的Intent中的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)) {
            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;
    }

    // 匹配Action
    public final boolean hasAction(String action) {
        return action != null && mActions.contains(action);
    }
    public final boolean matchAction(String action) {
        return hasAction(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) {
            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);
                    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;
    }

    //匹配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()) {
            final String category = it.next();
            if (!mCategories.contains(category)) {
                return category;
            }
        }

        return null;
    }    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值