Android中隐式Intent的匹配规则源码分析

2,484 阅读4分钟

Android中隐式Intent的匹配规则

我们从startActivity开始跟踪源码,看看系统是如何去做匹配规则的。

startActivity最终会调用到startActivityForResult ,然后又会调用到mInstrumentation.execStartActivity(),找到Instrumentation 类中的execStartActivity()方法,从这个方法开始,源码就开始慢慢变长了,我们要学会取舍了,看到跟我们想要的内容没关系的代码直接跳过,不然一下子就晕了,在这个方法中我们找到了关键代码。

  int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);

发现会走到 ActivityManager.getService().startActivity里面去,但是这个startActivity方法我们在Androidstudio中无法点击进去了,那我们得分析一下ActivityManager.getService()是啥了。点进去之后代码如下:

   public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b =ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

这里Singleton就是一个单例模式,是一个抽象类。当get()方法调用后,会回调他的实现类中的create,在这里就是这个 new Singleton() {},也就是说ActivityManager.getService() 拿到的是IActivityManager的对象am,熟悉源码和activity的启动流程的同学就明白这个东西就是我们的ActivityManagerService(AMS),在这里不做过多的探讨这个,那我们就去AMS中找到startActivity(),我们继续跟着流程走就到了mActivityStarter.startActivityMayWait(),进入到这个方法之后找到了一个非常关键的代码

ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {    synchronized (mService) {        return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,                PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags                | ActivityManagerService.STOCK_PM_FLAGS, userId);    }}

mService.getPackageManagerInternalLocked().resolveIntent,这个方法无法直接点击进去了。那我们就要去研究一下mService.getPackageManagerInternalLocked()返回的是什么了,点进去发现他返回的是PackageManagerInternal,这个类都无法点进去,只能搜索,找到之后我发现他是一个抽象类,且注释了一句Package manager local system service interface.包管理的本地服务接口,我们都知道系统的包管理都是PackageManagerService(PMS)实现的。然后我去PMS中搜索,果然搜到了PackageManagerInternal的实现类PackageManagerInternalImpl,找到他的resolveIntent方法,发现调用的是resolveIntentInternal方法,然后我们忽略无用内容,一眼看到queryIntentActivitiesInternal方法,点进去查看找到

List<CrossProfileIntentFilter> matchingFilters =
                        getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);

从命名我们可以看出来是匹配filter去了,继续跟进。在IntentResolver中找到了queryIntent方法,这个方法的意思就是在拆解Intent拿到那些信息如;categories action,sheme等,然后在取做匹配。可以看到拿到这些信息后调用了buildResolveList发放,点进去就找到了我们像要分析的方法

match = filter.match(action, resolvedType, scheme, data, categories, TAG);

filter就是IntentFilter,看看他的实现

 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的匹配规则 一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(区分大小写),action方面就匹配成功。可通过setAction方法为Intent设置action,也可在构造Intent时传入action。需要注意的是,隐式Intent必须指定action(如不指定action则必须指定data或mimetype,这种情况下,只要IntentFilter至少含有一个action就可以匹配)。

category的匹配规则 Intent中的category必须都在Intent Filter中出现才算匹配成功。Intent可以不指定category,若Intent中未指定category,系统会自动为它带上“android.intent.category.DEFAULT”。所以,想要接收隐式Intent的Component都必须在manifest文件中的Intent Filter声明中带上“android.intent.category.DEFAULT”,我们可以通过addCategory方法为Intent添加category。

data的匹配规则 data可进一步分为uri(由scheme、host、port、path | pathPattern | pathPrefix这4部分组成)和mimetype。 Intent的uri可通过setData方法设置,mimetype可通过setType方法设置。同action类似,只要Intent的data只要与Intent Filter中的任一个data声明完全相同,data方面就匹配成功。

结论是网上找的,我的目的是分析源码。了解流程,从matchAction matchData matchCategories方法中可以得出结论。上述流程中有两次关键的跳转,一次从activity调到AMS,然后从AMS调到PMS,需要对系统安装应用和activity的启动流程有一定的了解。应用的安装实际上就是PMS解析了所有的apk文件里的manifest文件,把里面的所有信息全部保存下来了。所以想要去匹配intent 肯定是要到PMS中取拿信息的。