In-depth analysis of Intent matching queries, in-depth analysis of Intent matching

Source: Internet
Author: User

In-depth analysis of Intent matching queries, in-depth analysis of Intent matching

Respect Originality:Http://blog.csdn.net/yuanzeyao/article/details/42243583


In the previous article, we analyzed in detail the Startup Process of PackageManagerService (for convenience, I will refer to PackageManagerService as PMS, scan System apps and installed apps, and save the app information to some data structures. In this article, we will continue to analyze the Intent matching Query Process in the previous article. If you are not familiar with PMS, we suggest you read the PackageManagerService Startup Process Analysis in the previous article.


Developed as an Android App, I believe you are not familiar with Intent. For example, if I want to start another Activity in an Activity, the following code will be used:

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

The above method is called the display Intent call. Of course, we will use the implicit Intent sometimes, for example:

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

Because Intent is very simple to use, I don't want to spend too much time describing it here, from the source code perspective, we need to understand how Intent matches Acitivity (the principle of aggreger is similar ).


Let's start with the startActivity function directly (Note: I am using the 4.1 source code, and the source code of different versions will be somewhat different). Here, we will first provide a time sequence diagram and then follow the time sequence diagram to see the source code.


Figure 1-1

According to Figure 1-1, when we call the startActivity method of Activity, we actually call the startActivity method of ContextImpl.

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

In the startActivity method of ContextImple, The execStartActivity method of Instrumentation is called. I will not post the source code for this method. In this method, the startActivity method of ActivityManagerService is actually called, this method actually calls the startActivityMayWait method of ActivityStack. This method calls its own resolveActivity method, and finally calls the PMS resolveIntent method. Now we finally see PMS, in the resolveIntent method, the queryIntentActivities method is called. queryIntentActivities returns an ActivityInfo object. We know that an ActivityInfo object is an Activity object and records all the information of an Acitivity object. The source code of queryIntentActivities is provided here.

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>();        }    }
For the above code, we can see that:

If the Intent specifies Componet, ActivityInfo can be found directly through Componet.

If packagename is specified, you can find the Package through packagename, and then match the Package by using the Activities contained in the Package.

If none of them meet the requirements, the system-wide matching is required.


Here, we need to recall some important data structures in the previous article.

Recall 1: PackageManagerService has two scanPackageLI. The first scanPackageLI parameter is File. Its job is to parse the AndroidManifest. xml File of the specified File (apk) into a PackageParser. Package object. Let's take a look at the fields of this object.


Figure 1-2


Here I only list the important fields. I believe you will understand that the Package stores all the information in an apk, including all the activities and services. In PMS, A HashMap stores all Pacakge, and the key is the package name.


Recall 2: In the second scanPackageLI, some information in the specified Package is publicly available. For example, all activities in activities are added to the ActivityIntentResolver variable mActivities. Note that the Activity mentioned here is not of the same type as the Activity we usually use. Its Inheritance structure is as follows:


Figure 1-3


Recall 3: In scanPackageLI, the specified PackageParser. Activity is saved by calling the addActivity method of ActivityIntentResolver. Let's see what addActivity has done.

/*** @ Param a * Activity * @ param type * "activity" or "recevier" */public final void addActivity (PackageParser. activity a, String type) {final boolean systemApp = isSystemApp (a.info. applicationInfo); // save it to mActivities in a HashMap. put (. getComponentName (), a); final int NI =. intents. size (); // traverse all intentfilters in the Activity, and then call the addFilter method to save for (int j = 0; j <NI; j ++) {PackageParser. activityInt EntInfo 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" +. 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 );}}

The logic is relatively simple. You can directly access the addFilter function to see what you have done.

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 is a HashSet type variable. This method first saves the ActivityIntentInfo type variable to mFilters, and then calls the register_intent_filter method.

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

Before reading the code, you need to be familiar with the data structure here. The filter is equivalent to an IntentFilter. I is an iterator. through which we can traverse all the scheme of the filter, and dest is a HashMap, the key is the scheme of the filter, and the value is an ArrayList <F>. In fact, it traverses all scheme of an IntentFilter and finds the corresponding ArrayList based on this scheme <F>, put the Filter into ArrayList <F> and return the number of scheme.


Return to the addFilter method, and then call the register_mime_types method. Similarly, let's see what this method has done.

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

The functions of this method are the same as those of the register_intent_filter method, except that register_intent_filter processes scheme, where type is processed and the logic of type is more complex than scheme. Scheme only uses one mSchemeToFilter for storage, and type uses three. They are:

MWildTypeToFilter: used to save the IntentFilter set to "image/*" of the Data type. However, setting "image/jpeg" is not included.

MTypeToFilter: Contains mWildTypeToFilter and IntentFilter information indicating that the Data type is a definite parameter, such as "image/jpeg" and "image /*".

MBaseTypeToFilter: stores the IntentFilter of the Base type in MIME, but does not include the IntentFilter of the Sub type "*".



In fact, the three points recalled above are the content of the previous article. Let's start to study the logic of 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>();        }    }

This part of the code logic is not complicated. You can get the ComponetName through Intent. If the ComponetName is not null (it indicates that the display call is used), you can call the getActivityInfo method to get ActivityInfo. GetActivityInfo obtains the PackageParser. Activity object based on the ComponetName in mActivities, and changes the PackageParser. Activity object to the ActivityInfo object by calling PackageParser. generateActivityInfo method. If ComponetName is null (implicitly called), there are two possible situations:

First case: Get the package name Null through intent, then call the queryIntent method of ActivityIntentResolver

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

Few codes. Call the queryIntent of IntentResolver and check the source code of 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;    }


This function looks complicated, but the logic is very simple. I will briefly describe it here.

First, if the given Intent contains MIME, it will match the qualified IntentFilter in the above (mTypeToFilter, mWildTypeToFilter, mBaseTypeToFilter) and save the results to firstTypeCut, secondTypeCut, thirdTypeCut, then match based on scheme, save the result to schemeCut, call the buildResolveList method, and then match the action, scheme, categories, and other factors to save the result to finalList, finally, sort finalList. The analysis is complete.


Case 2: If the package name in intent is not Null, obtain PackageParser Based on the package name. package object. You can call the queryIntentForPackage method of ActivityIntentResolver to traverse PackageParsr. the mactivities object in the Package. all intentfilters in the Activity are added to listCut (an ArrayList), and The queryIntentFromList method of IntentResolve is called. In the queryIntentFromList method, according to the specified Intent action, categories, scheme, type and other information match the IntentFilter object in listCut.


Let's write about the Intent matching process here.





Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.