美文网首页
IntentFilter的匹配逻辑源码分析.md

IntentFilter的匹配逻辑源码分析.md

作者: zivxia | 来源:发表于2020-04-18 13:21 被阅读0次

    我们都知道Activity启动分为显示启动和隐示启动,其中显示启动很好理解就是根据指定的类的class进行启动,很好就行匹配找到目标activity,下面先来看下显示启动的匹配逻辑

        /**
         * Create a new component identifier from a Context and Class object.
         *
         * @param pkg A Context for the package implementing the component, from
         * which the actual package name will be retrieved.
         * @param cls The Class object of the desired component, from which the
         * actual class name will be retrieved.
         */
        public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) {
            mPackage = pkg.getPackageName();
            mClass = cls.getName();
        }
    

    再来看下ComponentName的euqlas的方法

        @Override
        public boolean equals(Object obj) {
            try {
                if (obj != null) {
                    ComponentName other = (ComponentName)obj;
                    // Note: no null checks, because mPackage and mClass can
                    // never be null.
                    //mPackage包名,mClass类的全路径名
                    return mPackage.equals(other.mPackage)
                            && mClass.equals(other.mClass);
                }
            } catch (ClassCastException e) {
            }
            return false;
        }
    

    现在就很清楚了,显示启动匹配是按照包名以及目标类的全路径名。
    显示启动的匹配规则已经很清楚了,下面来看下隐示启动的匹配逻辑,隐示启动是通过IntentFiler来指定匹配规则的,IntentFiler中有三个参数action,categories和data,然后data里面又包含MIME type,Uri scheme,host name等等。下面来直接看看下IntentFiler中match源码

        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的匹配方法matchAction

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

    从代码中可以看出,mActions是指IntentFiler中的action,也就是我们在AndroidManifest中定义的action。Intent中只能定义一个action,一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(注意,区分大小写,大小写不同但字符串内容相同也会造成匹配失败),action方面就匹配成功。

    然后再来看下categories的匹配规则,matchCategories

        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;
        }
    

    category在IntentFiler中和在Intent中都可以指定多个,这个mCategories就是IntentFiler中指定的category,matchCategories中的参数就是Intent中指定的category。从上面代码逻辑可以看出,只要Intent中指定的category在IntentFiler中都能找到就算匹配成功。

    最后再来看下相对复杂一点的data的匹配逻辑,matchData

       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;
        }
    

    首先data也是在IntentFiler中指定多个,在Intent中只能指定一个,首先来看第一个判断

     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);
            }
    
    image.png

    意思就是在IntentFiler中未指定任何的type和scheme,并且Intent中也未指定type和data,这时候直接就匹配成功了。接着当Intent定义的schemes不为空时,就判断如果包含Intent中设置的scheme,那就表明scheme匹配成功,然后继续匹配sspPrefix,如果schemeSpecificParts为null,直接匹配host,host匹配不成功则直接退出。

        public final int matchDataAuthority(Uri data) {
            if (mDataAuthorities == null || data == null) {
                return NO_MATCH_DATA;
            }
            final int numDataAuthorities = mDataAuthorities.size();
            for (int i = 0; i < numDataAuthorities; i++) {
                final AuthorityEntry ae = mDataAuthorities.get(i);
                int match = ae.match(data);
                if (match >= 0) {
                    return match;
                }
            }
            return NO_MATCH_DATA;
        }
    

    host匹配成功就开始匹配path,IntentFilter未指定任何path的话,则直接匹配成功,如果有指定path,匹配成功到最后匹配MimeType,

       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;
                        }
    

    匹配MimeType

            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;
                }
            }
    
        public @NonNull Intent setType(@Nullable String type) {
            mData = null;
            mType = type;
            return this;
        }
    
        public @NonNull Intent setData(@Nullable Uri data) {
            mData = data;
            mType = null;
            return this;
        }
    
        public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
            mData = data;
            mType = type;
            return this;
        }
    

    其中设置type会将data直接置为null,设置data也会将type置为null,我的理解就是系统未避免我们传入的data和type不匹配,所以两者必须得同时设置, 这样就减少了出错的几率。

    相关文章

      网友评论

          本文标题:IntentFilter的匹配逻辑源码分析.md

          本文链接:https://www.haomeiwen.com/subject/iqsvvhtx.html