美文网首页
IntentFilter 匹配规则,源码解析

IntentFilter 匹配规则,源码解析

作者: Today_Now | 来源:发表于2018-12-24 10:20 被阅读0次

Android 中启动Activity 有两种方式
1.显式启动

Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);

2.隐式启动

      <activity
            android:name=".ui.activity.TestDemo">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="android.intent.action.VIEW" />
                <data
                    android:host="com.gl.demo"
                    android:scheme="example"
                    android:path="/TestDemo1"/>
            </intent-filter>
        </activity>


            String uri ="example://com.gl.demo/TestDemo1";
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
            context.startActivity(intent);
Activity 启动部分流程.png

上述流程图中

显式匹配 getActivityInfo ()

PackageManagerService.java

显式启动
 new Intent(this, SecondActivity.class); 构造的conmponentName
 public Intent(Context packageContext, Class<?> cls) {
        mComponent = new ComponentName(packageContext, cls);
    }


   @Override
    public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
           ....
        synchronized (mPackages) {
          //根据class name获取
            PackageParser.Activity a = mActivities.mActivities.get(component);

          //检查是否已安装并且可用
            if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
                PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                if (ps == null) return null;
             
           // Make shallow copies
                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
                        userId);//生成一个匹配的ActivityInfo
            }
        }
        return null;
    }

上述代码中,重要的是mActivities.mActivities

mActivities : PackageManagerService.ActivityIntentResolver

ActivityIntentResolver.png

mActivities.mActivities 的存储过程就是解析AndroidManifest.xml 中的<activity>标签解析并存储的过程,此处不在扩张这个过程。
下面摘出一些简要代码,便于读者简单了解一下mActivities 的数据来源。

PackageParser.java

private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
           if (tagName.equals("activity")) {//解析xml 中<activity>标签
                Activity a = parseActivity(owner, res, parser, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);
   }
}

PackageManagerService.java

   private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
            final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
            throws PackageManagerException {
            ....省略代码....
//此处的pkg 对应上段代码中的owner,pkg.activities 就是解析完manifest中所有的<activity>标签
            N = pkg.activities.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.activities.get(i);
                a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                        a.info.processName, pkg.applicationInfo.uid);
                mActivities.addActivity(a, "activity");
              ....省略代码....
            }
}

public final void addActivity(PackageParser.Activity a, String type) {
            mActivities.put(a.getComponentName(), a);
            ....省略代码....
        }

隐式匹配 queryIntent()

ActivityIntentResolver.png

ActivityIntentResolver.java

public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
            int userId) {
        String scheme = intent.getScheme();

        ArrayList<R> finalList = new ArrayList<R>();

        F[] firstTypeCut = null;
        F[] secondTypeCut = null;
        F[] thirdTypeCut = null;
        F[] schemeCut = null;

       .... 省略了 MIME_TYPE 的匹配

        // If the intent includes a data URI, then we want to collect all of
        // the filters that match its scheme (we will further refine matches
        // on the authority and path by directly matching each resulting filter).
 
   /**
     * All of the URI schemes (such as http) that have been registered.
     * private final ArrayMap<String, F[]> mSchemeToFilter = new ArrayMap<String, F[]>();
     */

        if (scheme != null) {
            schemeCut = mSchemeToFilter.get(scheme);
         }
       .... 省略代码...
        FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
       .... 省略代码...
        if (schemeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, schemeCut, finalList, userId);
        }
        sortResults(finalList);
        return finalList;
    }

  private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
            boolean debug, boolean defaultOnly,
            String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
        final String action = intent.getAction();
        final Uri data = intent.getData();
        final String packageName = intent.getPackage();

        final boolean excludingStopped = intent.isExcludingStopped();

        ....省略代码....

        final int N = src != null ? src.length : 0;
        boolean hasNonDefaults = false;
        int i;
        F filter;
        for (i=0; i<N && (filter=src[i]) != null; i++) {
            int match;
           ....省略代码....
          //隐式匹配的关键代码都在  filter.match 方法中  
            match = filter.match(action, resolvedType, scheme, data, categories, TAG);
            if (match >= 0) {
             //defaultOnly
                if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
                    final R oneResult = newResult(filter, match, userId);
                    if (oneResult != null) {
                        dest.add(oneResult);
                    }
                } else {
                    hasNonDefaults = true;
                }
            } 
        }
    }

上述代码中,隐式匹配的关键处理代码都在
filter.match(action, resolvedType, scheme, data, categories, TAG)
此处的filter 指向 F
在ActivityResolver 中F 即 PackageParser.ActivityIntentInfo

ActivityIntentInfo.png

所以filter.match ()方法最终指向了IntentFilter 中,终于看到了熟悉的面孔

IntentFilter.java

public final int match(String action, String type, String scheme,
            Uri data, Set<String> categories, String logTag) {
      
       //<action> 匹配
        if (action != null && !matchAction(action)) {
        }
      //<data> 匹配
        int dataMatch = matchData(type, scheme, data);
        if (dataMatch < 0) {
            return dataMatch;
        }
      //<category>匹配
        String categoryMismatch = matchCategories(categories);
        if (categoryMismatch != null) {
            return NO_MATCH_CATEGORY;
        }
        return dataMatch;
    }
<action>匹配规则

IntentFilter.java

  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>匹配规则

IntentFilter.java

  public final int matchData(String type, String scheme, Uri data) {
        final ArrayList<String> types = mDataTypes;//对应<data android:mimeType="*/*">
        final ArrayList<String> schemes = mDataSchemes;//对应<data android:schema="">

        int match = MATCH_CATEGORY_EMPTY;

        if (types == null && schemes == null) {
            return ((type == null && data == null)
                ? (MATCH_CATEGORY_EMPTY+MATCH_ADJUSTMENT_NORMAL) : NO_MATCH_DATA);
        }
      //schema 匹配成功,才继续;匹配失败则直接返回
        if (schemes != null) {
            if (schemes.contains(scheme != null ? scheme : "")) {
                match = MATCH_CATEGORY_SCHEME;
            } else {
                return NO_MATCH_DATA;
            }

            //ssp  android:sspPrefix="" 有ssp 标签的则此数组不为空,用的比较少,此处略
            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.
                //auth tag   <data>标签下有<android:host=""> mDataAuthorities 不为空
                final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
                if (authorities != null) {
                    int authMatch = matchDataAuthority(data);//host 匹配继续;失败
                    if (authMatch >= 0) {
                 // <data>标签下有<android:path=""> mDataPaths不为空
                        final ArrayList<PatternMatcher> paths = mDataPaths;
                        if (paths == null) {
                            match = authMatch;
                        } else if (hasDataPath(data.getPath())) {//path 匹配
                            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;
            }
        } 
      ....省略代码....

        return match + MATCH_ADJUSTMENT_NORMAL;
    }

从上述代码可以看出 data 匹配:
匹配 schema 成功,则继续匹配;否则不匹配;
匹配 host 成功,继续匹配;否则不匹配;
匹配 path 成功,继续匹配;否则不匹配

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

从上述代码可以看出,如果Intent 中包含的category 在IntentFilter 中没有,则匹配失败。

那么AndroidManifest.xml 中的<intent-filter>是如何对应解析到IntentFilter 中的呢,下面展示一些关键代码,便于大家与上面数据来源做对比。

 private boolean parseIntent(Resources res, XmlResourceParser parser,
            boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
            throws XmlPullParserException, IOException {

//IntentInfo extends IntentFilter ,outInfo  的方法对应到IntentFilter 中
          
          String nodeName = parser.getName();
            if (nodeName.equals("action")) {//<action>解析
                    ....
                outInfo.addAction(value);
            } else if (nodeName.equals("category")) {//<category>解析
                    ....
                outInfo.addCategory(value);
            } else if (nodeName.equals("data")) {
                sa = res.obtainAttributes(parser,
                        com.android.internal.R.styleable.AndroidManifestData);

                String str = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestData_mimeType, 0);
                if (str != null) {//<android:mimeType="">
                    try {
                        outInfo.addDataType(str);
                    } catch (IntentFilter.MalformedMimeTypeException e) {
                        outError[0] = e.toString();
                        sa.recycle();
                        return false;
                    }
                }

                str = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestData_scheme, 0);//<android:schema="">
                if (str != null) {
                    outInfo.addDataScheme(str);
                }
                                             ....
                String host = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestData_host, 0);//<android:host="">
                String port = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestData_port, 0);//<android:port="">
                if (host != null) {
                    outInfo.addDataAuthority(host, port);
                }

                str = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestData_path, 0);//<android:path="">
                                               ....
            }
}

相关文章

  • IntentFilter 匹配规则,源码解析

    Android 中启动Activity 有两种方式1.显式启动 2.隐式启动 上述流程图中 显式匹配 getAct...

  • IntentFilter的匹配规则

    允许转载请注明出处。 IntentFilter的匹配规则 对于初学者来说,学习IntentFilter的匹配规则还...

  • IntentFilter的匹配规则

    IntentFilter的匹配规则 IntentFilter中的过滤信息有action,category,data...

  • IntentFilter匹配规则

  • IntentFilter匹配规则

    首先废话一下,如果要写东西,csdn和简书还是老老实实选简书。不管什么,就冲csdn不会自动保存,简书会自动保存就...

  • IntentFilter匹配规则

    IntentFilter直译过来就是意图过滤器,我们可以通过它的匹配规则去打开我们想要打开的一类Activity,...

  • IntentFilter的匹配规则

    前言 我们知道,启动Activity分为两种,显示调用和隐式调用。这里主要介绍隐式调用的IntentFilter匹...

  • IntentFilter的匹配规则

    启动Activity分为两种,显示调用和隐式调用。显式调用需要明确的指定被启动对象的组件信息,包括包名和类名...

  • IntentFilter的匹配规则

    我们知道,Activity的启动模式分为两种,分别是显式启动和隐式启动。显式启动需要明确的指定被启动的对象的组件信...

  • IntentFilter 的匹配规则

    1. 显示调用与隐式调用   显示调用需要明确指定被启动对象的组件信息,包括类名与包名,隐式调用不需要明确指定组件...

网友评论

      本文标题:IntentFilter 匹配规则,源码解析

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