Foreword
This article mainly introduces Android Intent, and analyzes the Intent query matching process from the perspective of Android source code.
Intten introduction
Intent's Chinese means "intention", and intention is a very abstract concept. So how to instance the intention in the encoding design of Android? Therefore, the Android system clearly specifies that an Intent can be measured by two aspects.
Main attributes: including Action and Data. Among them, Action is used to represent the action intention expressed by the Intent, and data is used to represent the data operated by the action.
Secondary attributes: including Category, Type, Component, and Extras. The category represents category, Type represents the data type of data, and components can be used to specify the response of a specific Intent (for example, specifying the Intent to be a class class under a package), and Extras is used to carry other information.
There are two main types of Intent in the Android system, which shows the Intent (Explicit Intent) and hidden Intent (Implicit Intent).
Explicit Intent: This type of Intent clearly indicates which Component to find. In the code, you can lock the target object through SetClassName or SetComponent.
Implicit Intent: This type of Intent does not clearly indicate which component to start, but sets the Action, Data, and Category to make the system screen the appropriate Component.
Next, write two code examples to introduce the Expllicit Intent and IMPLICT Inent. The first is Explicit Intent:
Private void StartexplicitintentWithComponent () {Intent Intent = New Intent (); ComponentName Component = New ComponentName ("Com.example.photocrop", "com.ex" ample.photocrop.mainActivity "); Intent.setComponent (Component); StartActivity (INTENT) ;} Private Void StartexplicitintentWithClassName () {Intent Intent = New Intent (); Intent.setClassName ("com.example.photocrop", "com.example.p Hotocrop.mainActivity "); StartActivity (INTENT);}However, from the source code, I found that SetClassName also used ComponentName to achieve Explicit Intent. The source code is as follows:
Public Intent SetClassName (String PackaGename, String ClassName) {MCOMPONENT = New ComponentName (PackaGename, ClassName); RETURN This;}Then, a code example of the Implict Intent. Here I use an Activity to mark some Intent Filter as an example, and then write an INTENT to start it.
<Activity android: name = ". SendIntentType"> <intent-Filter> <action android: name = "justTest"/> <category android: name = "justcategory" </Intent-F ilter> </activity>
In AndroidManifest.xml currently applied, the Intent-Filter is added to the SendIntentType class. The name of Action is "Justtest", and the name of Category is "JustCategory". The code that starts the Activity is as follows:
Private void StartimpllicTintent () {Intent Intent = New Intent (); Intent.setAction ("JustAction"); Intent.adDcategory ("JustCategory"); StartaCTIVITY intent);}In the process of matching the Implict Intent process, the three items listed by Intent Filter will be used as the reference standard. The specific steps are as follows:
Management of ActivityI information
From the above analysis, it can be seen that in the process of system matching Intent, you first need to manage all Activity information in the current system. Activity's information was collected and managed by PackageManagerservice when scanning APK. The related source code is as follows:
// Process the attached attural information 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); ty (a, "activity");} In the above code, there are two important data structures, as shown in the figure below.
Combined with the data structure of the code and the above figure, it can be seen:
Macitivitys is the ActivityIntentResolver type. It is a member variable of PKMS to save all information related to Activity in the system. There is also a MACTIVIES variable inside this data structure. It uses componentName as key and saves the PackageParser.Activity object.
All information -related information obtained from the APK (including the IntentFilter label declared in XML) is saved by PackageParser.Activity.
The previous code calls the adDactivity function to complete the publicity of private information. The code of the addactivity function is as follows:
Public Final Void AddActivity (PackageParser.Activity A, String Type) {Final Boolean SystemApp = ISSYSTEMAPP (A.Info.ApplicationInfo); UT (a.getComponentName (), a); Final int ni = a.Intents.size ( ); Int j = 0; j <ni; j ++) {packageParser.activityInTINTINFO Intent = a.Intents.get (J); if (! SystemApp &&Getpriority ()> 0 && "Activit. y ".equals (Type ) {// non -system APK's priority must be 0 intent.setpriority (0);} AddFilter (INTENT);}}Let's take a look at the addfilter function. The function source code is as follows:
Public void addfilter (f F) {// mfilters to save all IntentFilter information mFILTERS.ADD (F); int Nums = register_intent_filter (f.Schemesitrator (), MSCHEMETOFILTER, "Scheme:"); int Numt = register_mime_types (f, "Type:"); if (nums == 0 && numt == 0) {register_intent_filter (F.ACTIONSITERTOR (), MACTINTOFILTER, "Action:");} if (numt! = 0) { Filter (F, F.ACTIONSITATRATOR (), MTYPEDACTINTOFILTER, "TypeDaction:");}}}}There are several more data structures here. They are similar to ArrayMap <string, F []>, where f is the template parameter.
After understanding the approximate data structure, let's take a look at the function implementation of register_intent_filter:
Private Final int register_intent_filter (Filter, Iterator <string> I, ArrayMap <string, F [] Dest, String, String, IF (i == NULL) {Return 0;} int Num = 0; While (i.hasnext ()) {String name = ipxt (); num ++; addFilter (Dest, name, file);} Return num;}Then it is another adDFILTER function, which is obviously a function heavy load. Let's take a look at the implementation of this addFilter:
Private Final Void Addfilter (ArrayMap <string, F [] Map, String name, Filter) {f [] array = map.get (name); if (array == null) {array = newsRay (2 ); map .put (name, array); Array [0] = Filter;} else {final n = array.length; int i = n; space (i> 0 && array [i-1] == null) {i- -;} if (i <n) {Array [i] = Filter;} Else {f [] newa = newsray ((n*3)/2); System.arrayCopy (Array, 0, NEWA, 0, n) ; newa [n] = filter; map.put (name, newa);}}}In fact, the code is still very simple. If the F array exists, the capacity is judged, and the capacity is not enough. If enough, find the position insertion. If the F array does not exist, create an array with a capacity of 2 and assign the No. 0 element to the Filter.
Intent matching query analysis
The client through the QueryIntentActivities function output by ApplicationPackageManager to initiate a query request to the PackageManageerService. The code is as follows: the code is as follows:
@Override Public List <Resolveinfo> QueryIntentActivities (INTENT Intent, Int Flags) {RETURN QURYINTITITITIESASER (Intent, Flags, MContext.get UserId ());} / ** @Hide Same as Above But for a Specific User* / @override Public List <Resolveinfo> QueryIntentActivitiesasuser (Intent Intent, Int Flags, Intimeid) Ifneeded (mcontext.getContentResolver ()), Flags, Userid);} Catch (RemoteException E) {Throw New RuntimeException ("Package Manager Has died", e);}}It can be seen that the real implementation of QueryIntentActivities is in the PackageManagerService.java. The function code is as follows:
Public list <ResolveInfo> QueryIntentActivities (Intent Intent, String Resolvedtype, Int Flags, Int Userid) {if (! Susermanager.exists)) Collections.emptyList (); EnforceCrossuserpermission (binder.GetCallinguid (), Userid, False, " Query Intent Activities "); ComponentNetName Comp = Intent.getComponent (); if (comp == null) {if (INTENT.GetSelector ()! = NULL) {Intent = Intent.getSelect or (); Comp = Intent.getComponent () ;}} if (Comp! = Null) {// Explicit's INTENT, directly obtain the corresponding ActivityInfo Final List <Resolveinfo> list = New ArrayInfo> (1); itInfo ai = getATIVITYINFO (Comp, Flags, userid); if (ai! = Null) {final resolveinfo Ri = new resolveinfo (); ri.ActivityInfo = AI; list.add (RI);} // reader synchronize D (mpackages) {final string pkganame = Intent.getpackage (); if (pkgname == null) {// Implicit Intent Return Mactivities.queryIntent (Intent, Resolvedtype, Flags, Userid);} Final Pa. ckageParser.package pkg = mpackages.get (pkgname); if (PKG! = NULL) {// Specify the package name of Intent Return Mactivities.quryIntentForpackage (Intent, Resolvedtype, Flags, PKG.Activities, Userid); IST <Resolveinfo> ();}}It can be seen that the implementation of Explicit Intent is relatively simple. Let's focus on the implementation of the Implict Intent. Implicit Intent called the QueryIntent method. Let's take a look at the implementation code of QueryIntent:
Public list <ResolveInfo> QueryIntent (Intent Intent, String Resolvedtype, Int Flags, Int Userid) {if (! SUSERMANAGER.ERID)) Return Null; GS = Flags; Return Super.queryIntent (Intent, ResolvedType, (Flags & PackageManager .Match_default_ONLY)! = 0, Userid);}Continue to track the QueryIntent method of IntentResolver.java, the source code is as follows:
Public list <R> QueryIntent (Intent Intent, String Resolvedtype, Boolean DEFAULTONLY, Intimeric) {String Scheme = Intent.GetScheme (); ArrayList <R> FINA llist = New ArrayList <r> (); // There are up to 4 rounds of matching Operation f [] firsttypecut = null; f [] seacondtypecut = null; f [] ThingthTypecut = NULL; e 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 = ResRINETYPE = Resring lvedtype.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); SecondTypecut = mwildyp ETOFILTER.Get (BaseType);} Else {// We can use anything with our base base. .get (BaseType); SecondTypecut = mwildypetofilter.get (BaseType);} // Any */ * Types Always Apply, but we only need to do this // if the Type was not almedy */ *. Thirdtypecut = mwildypetofilter. get ("*");} Else if (intents.getAction ()!! = Null) {// The Intent Specified Any Type ({@literal*}/*). Things, so as a first // Cut Let's use the action inc. Firsttypecut = MTYPEDACTOFILTER.get (Intent.getAction ()); ES A Data Uri, then we want to collect all of // The files that match its scheme (we will further refine matches // on the authority and path by directly matching each resulting). chemecut = mschemetofilter.get (scheme);} // if the intert DOES Not Specify Any Data - Either A Mime Type or // A URI - Then we will only be looking for matched empty //data. If (resolute == NULL && Intent.getAction ()! = NULL) {firsttypecut = MACTIONTOFILTER.Get (Intent.get.get ()); f (firsttypecut! = NULL) {BuildResolvelist (INTENT, CATEGORIES, Debug, DEFAULTONLY, Resolvedtype, Scheme , FirstTypecut, Finalist, Userid);} if (Secondtypecut! = Null) {BuildResolvelist (INTENT, CATEGORIES, Debug, DefaultOnly, SCHEME, SCHEME, S ECONDTYPECUT, FINALLIST, Userid);} if (Thirdtypecut! = Null) {BuildResolvelist (INTENT , Categories, Debug, DefaultOnly, Resolvedtype, Scheme, Thirdtypecut, Finalist, Userid);} if (schemecut! = NULL) {builtResolvelist (INTENT, CATE Gories, Debug, DEFAULTONLY, Resolvedtype, Scheme, Schemecut, Finalist, Userid);} Sortresults (Finalist); Return Finalist;}The specific query matching process is completed by the BuildResolvelist function. I do n’t post the code for the matching implementation of the query.