Explicit Intent and implicit Intent resolution

There are two types of Intents in Android:

  • Explicit Intent: Specifies the component to start by name (fully qualified class name). Typically, you use explicit intents to launch components in your own applications, because you know the class name of the Activity or service to launch. For example, start a new Activity in response to a user action, or start a service to download a file in the background.

  • Implicit Intent: Does not specify a specific component, but instead declares a general action to be performed, allowing components in other applications to handle it. For example, if you want to display a location on a map to the user, you can use an implicit Intent to ask another app with this feature to display the location on the map.

Displays the Intent to launch the current application component

An explicit Intent is typically invoked within the current application to launch the specified component of the current application. Several common instances of explicit Intent launches are shown below:

Intent = new Intent(this, testActivity.class); startActivity(intent);Copy the code
// An explicit Intent call --setComponent
ComponentName componentName = new ComponentName(this, TestActivity.class);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
Copy the code
// An explicit Intent call --setClass
Intent intent = new Intent();
intent.setClass(this, TestActivity.class);
startActivity(intent);
Copy the code
// An explicit Intent call --setClassName(packageContext, className)
Intent intent = new Intent();
//context, String
intent.setClassName(this, "com.tiny.demo.firstlinecode.test.view.TestActivity");
startActivity(intent);
Copy the code
// An explicit Intent call --setClassName(packageName, className)
Intent intent = new Intent();
//String, String
intent.setClassName("com.tiny.demo.firstlinecode"."com.tiny.demo.firstlinecode.test.view.TestActivity");
startActivity(intent);
Copy the code

Displays the Intent to launch other application components

Take a look at the error demonstration: Target Activity configuration: Do no additional configuration.

<activity
    android:name=".TestExplicitIntentActivity"
    android:label="TestExplicitIntentActivity" />
Copy the code
Intent Intent = new Intent(); // Start an Activity in another application. //String, String intent.setClassName("com.tinytongtong.dividerviewdemo"."com.tinytongtong.dividerviewdemo.TestExplicitIntentActivity");
startActivity(intent);
Copy the code

The specific errors are as follows:

The 2019-08-06 10:02:23. 355, 7230-7230 / com. Tiny. Demo. Firstlinecode E/AndroidRuntime: FATAL EXCEPTION: the main Process: com.tiny.demo.firstlinecode, PID: 7230 java.lang.SecurityException: Permission Denial: starting Intent { cmp=com.tinytongtong.dividerviewdemo/.TestExplicitIntentActivity } from ProcessRecord{2fe990c 7230:com.tiny.demo.firstlinecode/u0a397} (pid=7230, uid=10397) not exported from uid 10398 ... Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.am.ActivityStackSupervisor.checkStartAnyActivityPermission(Landroid/content/Intent; Landroid/content/pm/ActivityInfo; Ljava/lang/String; IIILjava/lang/String; ZZLcom/android/server/am/ProcessRecord; Lcom/android/server/am/ActivityRecord; Lcom/android/server/am/ActivityStack;) Z(libmapleservices.so:4243605) ...Copy the code

This SecurityException is completely avoidable. We set the Target Activity to the Android: Exported =”true” attribute.

<activity
    android:name=".TestExplicitIntentActivity"
    android:exported="true"
    android:label="TestExplicitIntentActivity" />
Copy the code

Then run again to successfully open the target Activity.

Of course, there is another way to open an Activity from another app, and we need to set an unrelated Activity for the target Activity. The configuration is as follows:

<activity
    android:name=".TestExplicitIntent1Activity"
    android:label="TestExplicitIntent1Activity">
    <intent-filter>
        <action android:name="com.tinytongtong.dividerviewdemo.action.TestExplicitIntent1Activity" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="com.tinytongtong.dividerviewdemo.category.TestExplicitIntent1Activity" />

        <data
            android:host="www.tiny.com"
            android:mimeType="text/plain"
            android:port="8080"
            android:scheme="http" />
    </intent-filter>
</activity>
Copy the code

Startup code:

Intent-filter Intent Intent = new Intent(); //String, String intent.setClassName("com.tinytongtong.dividerviewdemo"."com.tinytongtong.dividerviewdemo.TestExplicitIntent1Activity");
startActivity(intent);
Copy the code

The purpose of this statement is to prove that overt intEnts can start activities in other applications.

Using explicit intEnts to start activities in other applications is officially not recommended, and we generally don’t write it that way. Since both parameters to the Int #setClassName method we started with are strings, the package name of the target application and the full path of the target application are represented as strings, which is something we should try to avoid hard-coding. Any time the target Activity changes the class name, changes the package name, or moves its location, the startup code we wrote will fail, which is clearly not in accordance with our code specification.

Intent# setClassName source code:

public @NonNull Intent setClassName(@NonNull String packageName, @NonNull String className) {
    mComponent = new ComponentName(packageName, className);
    return this;
}
Copy the code

Therefore, when launching components in other applications, you should use implicit intents, specifically intent-filters for matching.

An implicit Intent starts an instance

An implicit Intent does not specify a specific component, but rather states a general action to be performed. Based on the content of the Intent, the system matches the corresponding Activity and launches it.

The website says:

When creating an implicit Intent, the Android system compares the content of the Intent to the intent-filter declared in the manifest file of other applications on the device to find the appropriate component to launch. If the Intent matches an intent-filter, the system launches the component and passes it an Intent object. If multiple Intent filters are compatible, a dialog box is displayed allowing the user to select the application to use.Copy the code

So an implicit Intent can launch components of either the current app or another app. Below are two of the simplest implicit intEnts to start an Activity.

An example of starting the current application component is as follows: target activity configuration:

<activity android:name=".kfysts.chapter01.intent.implicit.ImplicitIntentTestAActivity">
    <intent-filter>
        <action android:name="com.tiny.demo.firstlinecode.kfysts.chapter01.intent.implicit.action.a" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
Copy the code

Intent code:

Intent Intent = new Intent(); //action intent.setAction("com.tiny.demo.firstlinecode.kfysts.chapter01.intent.implicit.action.a"); //Category can be left out because Default is normally set in androidmanifest.xml, and Default is added to startActivity by Default.if(intent.resolveActivity(getPackageManager()) ! = null) { LogUtils.e("match success");
    startActivity(intent);
} else {
    LogUtils.e("match failure");
}
Copy the code

An example of launching other application components is as follows: Target activity configuration (other application) :

<activity
    android:name=".TestImplicitIntentActivity"
    android:label="TestImplicitIntentActivity">
    <intent-filter>
        <action android:name="com.tinytongtong.dividerviewdemo.action.a" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
Copy the code

Intent code:

Intent Intent = new Intent(); //action intent.setAction("com.tinytongtong.dividerviewdemo.action.a"); //Category can be left out because Default is normally set in androidmanifest.xml, and Default is added to startActivity by Default.if(intent.resolveActivity(getPackageManager()) ! = null) { LogUtils.e("match success");
    startActivity(intent);
} else {
    LogUtils.e("match failure");
}
Copy the code

IntentFilter Matching rule

Implicit Intent calls are divided into two parts, one is the configuration of the components in AndroidManifest and the other is the construction of the Intent object.

The target component can only be successfully launched if the Intent object we build matches the target component’s configuration.

So how do you match the configuration? So that’s the matching rule for IntentFilter.

There are three types of filtering information in action, category, and data. Here is an example of a filter rule:

<activity
    android:name=".IActivity"
    android:label="IActivity"
    android:launchMode="singleTask"> < intent - filter > < action android: name = "com. Tinytongtong. Dividerviewdemo. Action. 11"/ > < action android: name =" 22 ". Com. Tinytongtong dividerviewdemo. Action. />
        <category android:name="android.intent.category.DEFAULT"/ > < category android: name = "com. Tinytongtong. Dividerviewdemo. Category. 11" /> www.tiny.com" android:mimeType="text/plain" android:port="8080" android:scheme="http" />  Copy the code

Match rule

To match the filtering list, you must match action, category, and data in the filtering list at the same time. Otherwise, the matching fails.

There can be multiple actions, categories, and data in a filtering list. All actions, categories, and data constitute different categories respectively. Information in the same category constrains the matching process of the current category.

Only an Intent that matches both action, category, and data is considered a complete match. Only an Intent that matches all matches can successfully launch an Activity.

In addition, an activity can have multiple intent-filters. An intent that matches any set of intent-filters can successfully start the corresponding activity.

action

Action is a string that is case sensitive. Some actions are predefined, and you can also define your own actions in the application.

There can be multiple actions in an Intent. If the Intent action is the same as any action in the Intent, the match succeeds.

In addition, both the action in the Intent and the action in the Intent are required. This means that at least one action must be specified in the Intent.

category

Category is also a string and case sensitive. The system predefined some categories, and we can also define our own categories in the application.

We usually say categories have default values, Is due to the system in the calling startActivity or startActivityForResult will DEFAULT to Intent with the “android. Intent. The category. The DEFAULT” this category.

Therefore, the corresponding configuration must be added to the configuration; otherwise, the match will fail.

<intent-filter>
    ...
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
Copy the code

Intent, we can not set the category, because the system DEFAULT for we added “android. Intent. The category. The DEFAULT”. If we’re going to add a category, it’s going to have to match any of them, otherwise it’s going to fail.

data
The data of grammar

The data syntax looks like this:

<data android:scheme="string"

      android:host="string"

      android:port="string"

      android:path="string"

      android:pathPattern="string"

      android:pathPrefix="string"

      android:mimeType="string" />
Copy the code

Data consists of two parts, mimeType and URI. MimeType refers to the media type, such as image/ JPEG, audio/ MPEG4-generic, and video/*. It can refer to different media formats, such as pictures, texts, and videos.

A URI contains a large amount of data and is structured as follows:

<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
Copy the code

Specific examples are as follows:

content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
Copy the code

Here’s what each of these numbers means:

① Android: Scheme URI mode, such as HTTP, file, content, etc. If scheme is not specified in the URI, then the other parameters of the entire URI are invalid, which also means that the URI is invalid.

② Android :host URI host name, such as www.baidu.com. If host is not specified, the other parameters in the entire URI are invalid, which also means that the URI is invalid.

Android:port the port number in the URI, such as 80, is only meaningful if scheme and host parameters are specified in the URI.

Android :path, Android :pathPrefix, and Android :pathPattern describe the path information. Path indicates the complete path information. PathPrefix indicates the pathPrefix. The pathPattern also represents the complete path information, but it can contain the wildcard “*”, which means zero or more arbitrary characters. Note that due to the regular expression specification, if you want to represent a real string, “*” should be written as “\\*”, and “\” should be written as “\\\\”.

In addition, there are two special ways to write data: the following two ways are equivalent.

<intent-filter . . . >
    <data android:scheme="something" android:host="project.example.com" />
    . . .
</intent-filter>
Copy the code
<intent-filter . . . >
    <data android:scheme="something" />
    <data android:host="project.example.com" />
    . . .
</intent-filter>
Copy the code
Data matching rules

Data is optional and may not be set. However, if data is defined, then the Intent must also set matching data.

Take a look inside data:

The URI has default values file and content. If the URI is set, the default values are invalid.

MimeType may not be set.

The matching of data means that both mimeType and URI match.

Taking all of the above into account, here are several cases:

(1) Only mimeType is configured in data:

<intent-filter>
    ...
    <data android:mimeType="image/*" />
</intent-filter>
Copy the code

Since only mimeType is configured here, the default URI is used, and the scheme of the default URI is file or content.

So use the following two pieces of code to match:

intent.setDataAndType(Uri.parse("content://maolegemi"), "image/jpeg");
Copy the code
Intent.setdataandtype (uri.parse (intent.intent.setDataAndType)); //"file://maolegemi"), "image/jpeg");
Copy the code

(2) Only URI is configured in data:

<intent-filter>
    ...
    <data
        android:host="www.tiny.com"
        android:port="8080"
        android:scheme="http" />
</intent-filter>
Copy the code

The corresponding matching code is as follows:

intent.setDataAndType(Uri.parse("http://www.tiny.com:8080/abcdefg"), null);
Copy the code

③ Data is configured with both mimeType and URI:

<intent-filter>
    ...
    <data
        android:host="www.tiny.com"
        android:port="8080"
        android:mimeType="text/plain"
        android:scheme="http" />
</intent-filter>
Copy the code

The corresponding matching code is as follows:

intent.setDataAndType(Uri.parse("http://www.tiny.com:8080/abcdefg"), "text/plain");
Copy the code

conclusion

To sum up, the essential configuration for this is the default category.

For intEnts, action is essential because the default category is added.

If data is defined, the URI must be set in the Intent, regardless of whether mimeType is set, because the URI has a default value.

reference

Android development art exploration

Developer.android.com/guide/compo…

Developer.android.com/guide/topic…