Objective
This article mainly introduces the Android Intent, and analyzes the Intent query matching process from the point of view of the Android source code.
Intent Introduction
Intent's Chinese is meant to be "intent," and intent is a very abstract concept, so how do you instantiate the intent in Android's coding design? So the Android system explicitly specifies a intent that can be measured by two attributes.
Main properties: Includes action and data. Where action is used to represent the intent of the action expressed by the intent, and data is used to represent what is being manipulated by the action.
Secondary properties: Includes category, Type, component, and extras. Where category represents a class, type represents the MIME type of the data, component can be used to specify the responder of a particular intent (for example, to specify intent as a class class under a package), extras is used to host additional information.
There are two main types of Intent in the Android system, showing Intent (Explicit Intent) and implicit Intent (implicit Intent).
Explicit Intent: This kind of Intent clearly indicates which component to look for. The target object can be locked in code by setclassname or SetComponent.
Implicit Intent: This type of Intent does not explicitly indicate which component to start, but instead sets the action, Data, and category to allow the system to filter out the appropriate component.
Next, write two code examples to introduce explicit intent and implict inent. The first is explicit Intent:
private void Startexplicitintentwithcomponent () {
Intent Intent = new Intent ();
ComponentName component = new ComponentName ("Com.example.photocrop", "com.example.photocrop.MainActivity");
Intent.setcomponent (component);
StartActivity (intent);
}
private void Startexplicitintentwithclassname () {
Intent Intent = new Intent ();
Intent.setclassname ("Com.example.photocrop", "com.example.photocrop.MainActivity");
StartActivity (intent);
}
However, from the source inside to see, found Setclassname is also through the 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, give a code example of a implict intent. Here I use an activity to mark some intent filter as an example, and then write a intent to start it.
<activity
android:name= ". Sendintenttype ">
<intent-filter >
<action android:name=" justtest "/>
<category Android:name= "Justcategory"/>
</intent-filter>
</activity>
In the current application of Androidmanifest.xml, the Sendintenttype class was added with the intent-filter,action name "Justtest", and category's name was "Justcategory". The code to start the activity is as follows:
private void Startimplictintent () {
Intent Intent = new Intent ();
Intent.setaction ("Justaction");
Intent.addcategory ("Justcategory");
StartActivity (intent);
}
In the process of matching implict intent, the 3 items listed in intent Filter will be used as the reference standard, the following steps are as follows:
- Matches the Intentfilter action first, and if the intent set action does not satisfy the Intentfilter action, the match fails. If Intentfilter does not set an action or the set action is the same, the match succeeds.
- Then check the Intentfilter category, the matching method is the same as the action, the only exception is when category is Category_default.
- Finally, check data.
Management of Activityi information
from the analysis above, we can see that in the process of matching intent, we first need to manage all activity information in the current system. The activity information is collected and managed by the Packagemanagerservice when scanning apk. Relevant source code is as follows:
Process the activity information of the package
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");
}
In the code above, there are two more important data structures, as shown in the following illustration.
Combining the code and the data structure of the above diagram, we know:
Macitivitys is a activityintentresolver type and is a PKMS member variable that holds all activity-related information in the system. There is also a mactivities variable inside this data structure that holds the Packageparser.activity object with ComponentName as key.
All acitivity-related information that is parsed from apk, including Intentfilter tags declared in XML, is saved by packageparser.activity.
Calling the Addactivity function in the preceding code completes the public ownership of private information. The code for the Addactivity function is as follows:
Public final void Addactivity (packageparser.activity A, String type) {
final Boolean Systemapp = Issystemapp (a.info.a Pplicationinfo);
Mactivities.put (A.getcomponentname (), a);
Final int NI = A.intents.size ();
for (int j = 0; J < NI; J +) {
Packageparser.activityintentinfo intent = A.intents.get (j);
if (!systemapp && intent.getpriority () > 0 && "Activity". Equals (Type)) {
//non-system APK priority must be 0 C8/>intent.setpriority (0);
}
AddFilter (intent);
}
Next look at the addfilter function. Function source code is as follows:
public void addfilter (f f) {
//Mfilters Save all Intentfilter information
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:" );
}
}
There are several data structures, similar to those of Arraymap<string, f[]>, where F is the template parameter.
- Mschemetofilter: Used to save the Intentfilter information in the URI that is associated with scheme.
- Mactiontofilter: Used to save intentfilter information that sets only the action condition.
- Mtypedactiontofilter: Used to save intentfilter information that has both the action set and the MIME type of data set.
After you know the approximate data structure, let's take a look at the Register_intent_filter function implementation:
Private final int Register_intent_filter (F filter, iterator<string> I,
arraymap<string, f[]> dest, String prefix {
if (i = = null) {return
0;
}
int num = 0;
while (I.hasnext ()) {
String name = I.next ();
num++;
AddFilter (dest, name, filter);
return num;
}
And then another addfilter function, which is obviously a function overload, let's take a look at the implementation of this addfilter:
Private final void AddFilter (arraymap<string, f[]> map, String name, F filter) {
f[] array = map.get (name);
if (array = = NULL) {
array = NewArray (2);
Map.put (name, array);
Array[0] = filter;
else {
final int N = Array.Length;
int i = N;
while (i > 0 && array[i-1] = = null) {
i--
}
if (I < N) {
Array[i] = filter;
} else {
f[] Newa = NewArray ((n*3)/2);
System.arraycopy (array, 0, Newa, 0, N);
Newa[n] = filter;
Map.put (name, Newa);
}
}
In fact, the code is very simple, if the F array exists, then judge the capacity, not enough to expand, enough to find the location to insert. If the F array does not exist, create an array with a capacity of 2 and assign the number No. 0 element to that filter.
Intent Matching Query Analysis
the client initiates a query request to packagemanagerservice through the queryintentactivities function of the Applicationpackagemanager output, as follows:
@Override public
list<resolveinfo> queryintentactivities (Intent Intent,
int flags) {
return Queryintentactivitiesasuser (Intent, Flags, Mcontext.getuserid ());
}
/** @hide Same as above but for a specific user */
@Override public
list<resolveinfo> Queryintentactivitiesa Suser (Intent Intent,
int flags, int userId) {
try {return
mpm.queryintentactivities (
Intent,
Intent.resolvetypeifneeded (Mcontext.getcontentresolver ()),
flags,
userId);
} catch ( RemoteException e) {
throw new RuntimeException ("Package manager has died", e);
}
As you can see, the real implementation of Queryintentactivities is in Packagemanagerservice.java, and the function code is as follows:
Public list<resolveinfo> queryintentactivities (Intent Intent, String resolvedtype, int flags, int userId) {if
(!susermanager.exists (userId)) return collections.emptylist ();
Enforcecrossuserpermission (Binder.getcallinguid (), UserId, False, "query intent Activities");
ComponentName comp = intent.getcomponent ();
if (comp = = null) {if (intent.getselector ()!= null) {intent = Intent.getselector ();
comp = Intent.getcomponent (); } if (comp!= null) {//explicit intent, directly component to Activityinfo final LIST<RESOLV
einfo> 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) {//implicit Intent return Mactivities.queryintent (Intent, Resolvedtype, flags, US
Erid);
Final Packageparser.package pkg = Mpackages.get (pkgname); if (pkg!= null) {//Specifies the package name of the intent return Mactivities.queryintentforpackage (intent, resolvedtype, flags,
Pkg.activities, userId);
Return to New arraylist<resolveinfo> ();
}
}
We can see that the implementation of Explicit intent is simpler, we focus on implict intent implementation. Implicit intent called the Queryintent method, let's take a look at the implementation code for Queryintent:
Public list<resolveinfo> queryintent (Intent Intent, String resolvedtype, int flags, int userId) {
if (!suserman Ager.exists (userId)) return
null;
Mflags = flags;
Return Super.queryintent (Intent, Resolvedtype, (Flags & Packagemanager.match_default_only)!= 0, userId);
Continue to trace to the Intentresolver.java Queryintent method, the source code is as follows:
Public list<r> queryintent (Intent Intent, String resolvedtype, boolean defaultonly, int userId) {Strin
G scheme = Intent.getscheme ();
arraylist<r> finallist = new arraylist<r> ();
Up to 4 rounds of matching operations f[] firsttypecut = null;
f[] secondtypecut = null;
f[] thirdtypecut = null;
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 (SLAS hpos+1)!= ' * ') {//Not a wild card, so we can just look for all filters that//completely mat
Ch or wildcards whose base type matches. Firsttypecut = MtypetofiltEr.get (Resolvedtype);
Secondtypecut = Mwildtypetofilter.get (BaseType);
else {//We can match anything with our base type.
Firsttypecut = Mbasetypetofilter.get (BaseType);
Secondtypecut = Mwildtypetofilter.get (BaseType); //any */* types always apply, but we are need to does this//if the intent type is not already *
/*.
Thirdtypecut = Mwildtypetofilter.get ("*"); else if (intent.getaction ()!= null) {//The intent specified any type ({@literal *}/*).
This//can is a whole heck of a lot of things, so as a/I/s use the action instead.
Firsttypecut = Mtypedactiontofilter.get (Intent.getaction ()); }}//If The intent includes a data URI, then we want to collect all of//the filters that M Atch its scheme (we'll further refine matches//on the Authority and path by Directly matching each resulting filter).
if (scheme!= null) {schemecut = Mschemetofilter.get (scheme); }//If The intent does not specify any data--either a MIME type or//a URI--then we are only to be Looki
ng for matches against empty//data. if (Resolvedtype = = NULL && scheme = = null && intent.getaction ()!= null) {firsttypecut = Mactiont
Ofilter.get (Intent.getaction ());
} fastimmutablearrayset<string> categories = Getfastintentcategories (intent); if (firsttypecut!= null) {Buildresolvelist (Intent, Categories, Debug, Defaultonly, Resolvedtype, Schem
E, Firsttypecut, finallist, userId); } if (secondtypecut!= null) {Buildresolvelist (Intent, Categories, Debug, Defaultonly, Resolvedtyp
E, 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, S
Cheme, Schemecut, Finallist, userId);
} sortresults (Finallist);
return finallist;
}
The exact query matching process is done by the Buildresolvelist function. The matching implementation of the query I will not put the code, we go to the query to see the good.