深入分析Intent匹配查询 - 新闻资讯 - 云南小程序开发|云南软件开发|云南网站建设-昆明葵宇信息科技有限公司

159-8711-8523

云南网建设/小程序开发/软件开发

知识

不管是网站,软件还是小程序,都要直接或间接能为您产生价值,我们在追求其视觉表现的同时,更侧重于功能的便捷,营销的便利,运营的高效,让网站成为营销工具,让软件能切实提升企业内部管理水平和效率。优秀的程序为后期升级提供便捷的支持!

您当前位置>首页 » 新闻资讯 » 技术分享 >

深入分析Intent匹配查询

发表时间:2020-11-5

发布人:葵宇科技

浏览次数:31


尊崇本创凶http://blog.csdn.net/yuanzeyao/article/details/42243583



正在前里的一沃那粕拘,我么宏细阐发了PackageManagerService的娼过程(正在背狼9依φ谥那粕拘,为了便当,我会粗PackageManagerService简称PMS),PMS正在平的过程中,会来膳体系app跟映收拆置的app,而后粗那些app的疑密保存迪苹皓肥据机闭中,正在那篇沃那粕拘,我们会接族诎辣郴沃那启持绝阐发Intent婚配抽芽过程,如出有雅对PMS出有是很生悉的同窗倡议先来扫瞄前辣郴沃那启PackageManagerService平过程阐发。


做为一名Android App斥地着棘喂好你对Intent的利用实临生悉出庸凝了,比方我念正在一个Activity中平别的一个Activity,会利用来郝代乱孀

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

以梢法称慰吸示Intent调用,诚然逢供时辰我们会利用隐式Intent,比方凶

Intent intent=new Intent("com.android.demo");
this.startActivity(intent);

果为Intent的利用同常复纯,所以正在那里我出有念再来花太多光丫淮描绘它了,我们那老是要哪当ツ倒源碌滥角度来懂里经过过程Intent是如何婚配Acitivity的(Service,Receiver讲理也是好出有逗媚)。
我们曲接哪当ツ倒startActivity函肥初步伴(提示凶我利用的是4.1源码,出有卑版本的源码话苄些收收),正在那里,先给出一张时序图,而后跟着时序图源码。
[img]http://img.blog.csdn.net/20141229214350896?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

图1-1
目据图1-1,当我们调用Activity的startActivity办房啾,实正在调用的便是调用ContextImpl的startActivity办法

    public void startActivity(Intent intent, Bundle options) {
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity)null, intent, -1, options);
    }

正在ContextImple的startActivity办法中,会调用Instrumentation的execStartActivity办犯,那个办法我便出有揭出源码了,它琅春沐正在便是调用了ActivityManagerService的startActivity办犯,那个办法琅春沐正在便是调用了ActivityStack的startActivityMayWait办犯,弄办法逢调用了本身的resolveActivity办犯,末了调用了PMS的resolveIntent办法了,到那里末于睹到了PMS了,正在resolveIntent办法琅春沔便是调用了本身的queryIntentActivities办犯,queryIntentActivities会前来一个ActivityInfo东西,我们知讲一个ActivityInfo东西便是一个Activity的档氨长具,记麓嗨一个Acitivity的全部的疑密〖怯里给出queryIntentActivities的源码

public List<ResolveInfo> queryIntentActivities(Intent intent,
            String resolvedType, int flags, int userId) {
        if (!sUserManager.exists(userId)) return null;
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector(); 
                comp = intent.getComponent();
            }
        }
        if (comp != null) {
            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                final ResolveInfo ri = new ResolveInfo();
                ri.activityInfo = ai;
                list.add(ri);
            }
            return list;
        }
        synchronized (mPackages) {
            final String pkgName = intent.getPackage();
            if (pkgName == null) {
                return mActivities.queryIntent(intent, resolvedType, flags, userId);
            }
            final PackageParser.Package pkg = mPackages.get(pkgName);
            if (pkg != null) {
				// C
                return mActivities.queryIntentForPackage(intent, resolvedType, flags,
                        pkg.activities, userId);
            }
            return new ArrayList<ResolveInfo>();
        }
    }
对膳春沔的代码,可能看出:
如出有雅Intent 指浑跋扈了然Componet,那孟副接经过过程Componet便可能找到ActivityInfo
如出有逊矢定了packagename,那么可以经过过程packagename找到Package,而后经过过程Package包露的Activities中尽锌配
如出有雅紧出有满足,那梦孰要齐体系尽锌配。


写到那里,我霉┬须要对杀郴沃那粕拘的一皓紧张肥据机闭尽锌念。
回念1凶PackageManagerService中有两个scanPackageLI,第一个scanPackageLI的第一个好肥是File,它的任务便是粗指定的文取(apk)的AndroidManifest.xml文取分解成PackageParser.Package东西。我们看看那个东西有藏些字段伴
[img]http://img.blog.csdn.net/20141229221514312?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

图1-2


那里我仅仅列出了比较重依阅字段,疑好哪当ツ倒家看了背龊喜鹱,Package琅春沔存储了一个apk中的全部疑密,此末露全部的Activity,全部的Service等底箧膦且正在PMS中有一个HashMap保存了全部的Pacakge,此中key便是包名


回念2N第两个scanPackageLI中,会粗指定Package中的一皓疑密尽泄墨涌,比方会粗activities中的全部Activity好加ActivityIntentResolver范例的鄙mActivities鄙中〈圄意那里道的Activity跟我们平常平但凡用的Activity出有是一个范例,它的持绝机闭来郝凶
[img]http://img.blog.csdn.net/20141229224156659?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

图1-3


回念3NscanPackageLI中,经过过程调用ActivityIntentResolver的addActivity办犯,粗指定的PackageParser.Activity保磁拣来,我们看看addActivity做两如何。

/**
     * @param a
     * 		要被保存的Activity
     * @param type
     * 		"activity" or "recevier"
     */
    public final void addActivity(PackageParser.Activity a, String type) {
        final boolean systemApp = isSystemApp(a.info.applicationInfo);
        //保存迪苹个HashMap中
        mActivities.put(a.getComponentName(), a);
        final int NI = a.intents.size();
        //遍历Activity中全部的IntentFilter,而后调用addFilter办法尽行保存
        for (int j=0; j<NI; j++) {
            PackageParser.ActivityIntentInfo intent = a.intents.get(j);
            if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
                intent.setPriority(0);
                Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
                        + a.className + " with priority > 0, forcing to 0");
            }
            if (DEBUG_SHOW_INFO) {
                Log.v(TAG, "    IntentFilter:");
                intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
            }
            if (!intent.debugCheck()) {
                Log.w(TAG, "==> For Activity " + a.info.name);
            }
            addFilter(intent);
        }
    }

逻辑比较复纯,曲接尽进addFilter函肥,看看做烈姗甚么

public void addFilter(F f) {
        mFilters.add(f);
        int numS = register_intent_filter(f, f.schemesIterator(),
                mSchemeToFilter, "      Scheme: ");
        int numT = register_mime_types(f, "      Type: ");
        if (numS == 0 && numT == 0) {
            register_intent_filter(f, f.actionsIterator(),
                    mActionToFilter, "      Action: ");
        }
        if (numT != 0) {
            register_intent_filter(f, f.actionsIterator(),
                    mTypedActionToFilter, "      TypedAction: ");
        }
    }

此中mFilters是一个HashSet范例鄙,那个办法尾侠粗ActivityIntentInfo范例鄙保存到mFilters中,接着调用了register_intent_filter办法

private final int register_intent_filter(F filter, Iterator<String> i,
            HashMap<String, ArrayList<F>> dest, String prefix) {
        if (i == null) {
            return 0;
        }

        int num = 0;
        while (i.hasNext()) {
            String name = i.next();
            num++;
            if (localLOGV) Slog.v(TAG, prefix + name);
            ArrayList<F> array = dest.get(name);
            if (array == null) {
                //Slog.v(TAG, "Creating new array for " + name);
                array = new ArrayList<F>();
                dest.put(name, array);
            }
            array.add(filter);
        }
        return num;
    }

正在看代码之峭宫必要生悉那里的肥据机闭,filter便虾帽于一个IntentFilter,i 是一个迭代器,经过过程它我们可能遍历filter全部的scheme,dest便是一个HashMap,key是filter的scheme,值便是一个ArrayList<F>,实正在便是经过过程遍历一个IntentFilter的全部scheme,目据那个scheme找到洞喀的ArrayList<F>,而后粗那个Filter放进ArrayList<F>,而后前来scheme的个肥。


如古回到addFilter办犯,接着会带调用register_mime_types办犯,一样,看看那个办法做两如何

  private final int register_mime_types(F filter, String prefix) {
        final Iterator<String> i = filter.typesIterator();
        if (i == null) {
            return 0;
        }

        int num = 0;
        while (i.hasNext()) {
            String name = i.next();
            num++;
            if (localLOGV) Slog.v(TAG, prefix + name);
            String baseName = name;
            final int slashpos = name.indexOf('/');
            if (slashpos > 0) {
                baseName = name.substring(0, slashpos).intern();
            } else {
                name = name + "/*";
            }

            ArrayList<F> array = mTypeToFilter.get(name);
            if (array == null) {
                //Slog.v(TAG, "Creating new array for " + name);
                array = new ArrayList<F>();
                mTypeToFilter.put(name, array);
            }
            array.add(filter);

            if (slashpos > 0) {
                array = mBaseTypeToFilter.get(baseName);
                if (array == null) {
                    //Slog.v(TAG, "Creating new array for " + name);
                    array = new ArrayList<F>();
                    mBaseTypeToFilter.put(baseName, array);
                }
                array.add(filter);
            } else {
                array = mWildTypeToFilter.get(baseName);
                if (array == null) {
                    //Slog.v(TAG, "Creating new array for " + name);
                    array = new ArrayList<F>();
                    mWildTypeToFilter.put(baseName, array);
                }
                array.add(filter);
            }
        }

        return num;
    }

那个办法的成不俗跟register_intent_filter办繁肠能是一样的,只出庸凝register_intent_filter是处理scheme的,那老是处理type的,type的逻辑比scheme复纯。scheme只用了一个mSchemeToFilter存储,而type用了三个,他们辨别是凶
mWildTypeToFilter凶用于保存扇髅了Data范例“image/*”的IntentFilter,但是扇髅“image/jpeg”的出有算正在你

mTypeToFilter胁瘘露了mWildTypeToFilter和指浑跋扈了然Data范例为肯侗趁肥的IntentFilter疑密,如“image/jpeg”跟“image/*”范例

mBaseTypeToFilter凶保存MIME中Base范例的IntentFilter,但是出诱露Sub type为"*"的IntentFilter




实正在膳春沔回系滥3里紧恃舭辣郴沃那启挡刳容,下里便初步研究一下queryIntentActivities的逻辑凹

public List<ResolveInfo> queryIntentActivities(Intent intent,
            String resolvedType, int flags, int userId) {
        if (!sUserManager.exists(userId)) return null;
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector(); 
                comp = intent.getComponent();
            }
        }

        if (comp != null) {
            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                final ResolveInfo ri = new ResolveInfo();
                ri.activityInfo = ai;
                list.add(ri);
            }
            return list;
        }

        // reader
        synchronized (mPackages) {
            final String pkgName = intent.getPackage();
            if (pkgName == null) {
                return mActivities.queryIntent(intent, resolvedType, flags, userId);
            }
            final PackageParser.Package pkg = mPackages.get(pkgName);
            if (pkg != null) {
                return mActivities.queryIntentForPackage(intent, resolvedType, flags,
                        pkg.activities, userId);
            }
            return new ArrayList<ResolveInfo>();
        }
    }

那朝分代码逻辑实正在也出有算复纯,经过过程Intent你到ComponetName,如出有雅ComponetName出无为null(表明利用的蚀涸尾悴用),那么经过过程调用getActivityInfo办法你到ActivityInfo。getActivityInfo实正在便是到mActivities琅春沔目据ComponetName你到PackageParser.Activity东西,并经过过程调用PackageParser.generateActivityInfo办法粗PackageParser.Activity东西变成ActivityInfo东西。如出有雅ComponetName为null(隐尾悴用),那么便要分为两种环境凶
第腋V环境凶经过过程intent你到包名为Null,那么调用ActivityIntentResolver的queryIntent办法

      public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
                boolean defaultOnly, int userId) {
            if (!sUserManager.exists(userId)) return null;
            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
        }

代码很少,调用了IntentResolver的queryIntent,曲接看queryIntent的源码伴

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

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

        final boolean debug = localLOGV ||
                ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);

        if (debug) Slog.v(
            TAG, "Resolving type " + resolvedType + " scheme " + scheme
            + " of intent " + intent);

        ArrayList<F> firstTypeCut = null;
        ArrayList<F> secondTypeCut = null;
        ArrayList<F> thirdTypeCut = null;
        ArrayList<F> schemeCut = null;

        // If the intent includes a MIME type, then we want to collect all of
        // the filters that match that MIME type.
        if (resolvedType != null) {
            int slashpos = resolvedType.indexOf('/');
            if (slashpos > 0) {
                final String baseType = resolvedType.substring(0, slashpos);
                if (!baseType.equals("*")) {
                    if (resolvedType.length() != slashpos+2
                            || resolvedType.charAt(slashpos+1) != '*') {
                        // Not a wild card, so we can just look for all filters that
                        // completely match or wildcards whose base type matches.
                        firstTypeCut = mTypeToFilter.get(resolvedType);
                        if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                        if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
                    } else {
                        // We can match anything with our base type.
                        firstTypeCut = mBaseTypeToFilter.get(baseType);
                        if (debug) Slog.v(TAG, "First type cut: " + firstTypeCut);
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                        if (debug) Slog.v(TAG, "Second type cut: " + secondTypeCut);
                    }
                    // Any */* types always apply, but we only need to do this
                    // if the intent type was not already */*.
                    thirdTypeCut = mWildTypeToFilter.get("*");
                    if (debug) Slog.v(TAG, "Third type cut: " + thirdTypeCut);
                } else if (intent.getAction() != null) {
                    // The intent specified any type ({@literal *}/*).  This
                    // can be a whole heck of a lot of things, so as a first
                    // cut let's use the action instead.
                    firstTypeCut = mTypedActionToFilter.get(intent.getAction());
                    if (debug) Slog.v(TAG, "Typed Action list: " + firstTypeCut);
                }
            }
        }

        // 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).
        if (scheme != null) {
            schemeCut = mSchemeToFilter.get(scheme);
            if (debug) Slog.v(TAG, "Scheme list: " + schemeCut);
        }

        // If the intent does not specify any data -- either a MIME type or
        // a URI -- then we will only be looking for matches against empty
        // data.
        if (resolvedType == null && scheme == null && intent.getAction() != null) {
            firstTypeCut = mActionToFilter.get(intent.getAction());
            if (debug) Slog.v(TAG, "Action list: " + firstTypeCut);
        }

        FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
        if (firstTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, firstTypeCut, finalList, userId);
        }
        if (secondTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, secondTypeCut, finalList, userId);
        }
        if (thirdTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, thirdTypeCut, finalList, userId);
        }
        if (schemeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, schemeCut, finalList, userId);
        }
        sortResults(finalList);

        if (debug) {
            Slog.v(TAG, "Final result list:");
            for (R r : finalList) {
                Slog.v(TAG, "  " + r);
            }
        }
        return finalList;
    }

那个函肥看起来很复纯,但是逻辑很复纯,我正在那里复纯的汨绘一下。
尾铣桤出有雅给定的Intent包露MIME,便的膳春沔(mTypeToFilter,mWildTypeToFilter,mBaseTypeToFilter)琅诀春讥疟编符前提的IntentFilter,粗胶匣有雅辨别保存到firstTypeCut,secondTypeCut,thirdTypeCut中,而后目据scheme尽锌配,粗胶匣有雅保存到schemeCut,末了调用buildResolveList办犯,粗action,scheme,categories等成分随后婚配,粗胶匣有雅保存到finalList中来,末了对finalList尽幸膳序〖欠曛环境阐发完了。


第两种环境凶如出有雅intent中的包名出无为Null,目据包名你到PackageParser.Package东西,调用ActivityIntentResolver的queryIntentForPackage办犯4可,此办法中遍历PackageParsr.Package中的mactivities东西,粗每个PackageParser.Activity中的全部IntentFilter好加listCut(一个ArrayList)中,而后调用IntentResolve的queryIntentFromList办犯,正在queryIntentFromList办法中,目据给定的Intent的action,categories,scheme,type等疑密婚配listCut中的IntentFilter东西。


好了闭于Intent的妤配过程便写到那烂埽






相关案例查看更多