review

In the first four articles, we introduced the basics of the four components of Android, which form the basis of our App and are the best embodiment of Android system design. Components are completely decoupled, and if you want to access or launch other components, you can use intents to do so. Three of the four component types (Activity, Service, and Broadcast) can all be started with an asynchronous Intent. Intents bind components to each other at run time. So we can think of an Intent as a messenger between components (whether that component belongs to its own App or another App).

Each component has a different startup method:

  • To start an Activity, you can pass the Intent with startActivity() or startActivityForResult() (when you want the Activity to return a result).

  • To start a Service, you can start the Service by passing an Intent via startService() or bind to the Service by passing an Intent via bindService()

  • To send a Broadcast, you can pass the Intent through sendBroadcast(), sendOrderedBroadcast(), or sendStickyBroadcast()

As with activities, services, and broadcasts, contentProviders are not launched with intents. Instead, they are launched when they become the target of a request from the ContentResolver. To access the contents of the ContentProvider, the ContentResolver calls query(), INSERT (), Update (), delete(), and other methods.

directory

A, Intent

(1) Definition of Intent

An Intent is a messaging object that requests an action from another component, whether that component is the current application or another application. Specifically, there are three main uses: starting an Activity, starting or binding a Service, and passing a broadcast.

(2) Classification of Intents

There are two types of intents:

  • Explicit Intents: Specify the application that can handle an Intent by providing the package name of the target application or a fully qualified component class name. Typically, an explicit Intent is used to launch a component in your own application, because your application knows the class name of the Activity or Service it is launching.
  • Implicit Intent: Instead of specifying a specific component, it 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 request that another application that does so display the location on the map.

(3) Workflow of Intents

Suppose you have Activity A that needs to start Activity B. If you start with an Intent displayed, the system starts the component immediately. With implicit Intents, Android finds the component to launch by comparing the contents of the Intent to the Intent filters declared in the manifest of other applications on the device. If an Intent matches an Intent filter, the system starts the component and passes it an Intent object. If multiple Intent filters are compatible, a dialog box is displayed that allows users to select which application to use. The general process is as follows:

Special attention:

To ensure application security, you must start a Service with an explicit Intent and do not declare an Intent filter for the Service. Starting a service with an implicit Intent is a security hazard because there is no way to determine which services will respond to the Intent and the user cannot see which services are started. Starting with Android 5.0 (API level 21), an exception is thrown if bindService() is called with an implicit Intent.

(4) Construct the Intent

An Intent object carries information that Android uses to determine which component to launch. Specifically, it will carry the following seven information: ComponentName, Action, Category, Data, Type, Extra, Flags. We can call these seven types of information the seven properties of an Intent. The following is a detailed analysis of the meaning and usage of each attribute:

  1. ComponentName: Specifies the name of the component to be startedAn Intent that specifies ComponentName specifies which component it will launch, so it is called an explicit Intent. An Intent that does not specify ComponentName is called an implicit Intent. An implicit Intent does not specify which component to launch, and the application uses the other information specified by the Intent to launch the component. Example code:
// Declare a display intent
Intent intent = new Intent();
ComponentName componentName = new ComponentName(MainActivity.this,SecondActivity.class);
intent.setComponent(componentName);
startActivity(intent);
Copy the code

In addition to specifying the name of the component to launch the intent via setComponent, we can also use the constructor of the Intent:

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
Copy the code

Using the Intent constructor to set the name of the component makes the code more concise.

  1. Action: operationWhen we want to declare an implicit intent, we use an Action, which specifies the specific Action that the component being launched will perform. For example, if I want to launch an Activity that can view photos, I can set the Action toACTION_VIEW, or start an Activity that can send emails. You can set the Action to ‘

ACTION_SEND `. Actions are usually used in combination with categories. The Action itself is a string. In addition to being defined by the system, we can also define it ourselves. The following code demonstrates how to launch an Activity by defining an implicit Intent:

Step1: define an implicit Intent at the initiator

Intent intent = new Intent();
// Customize an action: com.cyy.jump
intent.setAction("com.cyy.jump");
// A category must be specified
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
Copy the code

Step2: configure

for the initiated party. This is done in the AndroidManifest.xml setting for the Activity to be launched:

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.cyy.jump" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
Copy the code

In the < Intent-filter >, we set two properties:

and

. The value of must be the same as the initiator’s setAction: com.cyy.jump.

Let’s run the project and see what happens:

The jump succeeds.

What happens if we configure the same for multiple activities?

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.cyy.jump" />

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

<activity android:name=".ThirdActivity">
    <intent-filter>
        <action android:name="com.cyy.jump" />

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

Let’s run it and see what it looks like:

As you can see, if there are more than one Activity in the same application with the same setting, after calling startActivity(), the system will pop up a selection box for the user to choose which Activity to jump to.

This is launching a page within the same app. What if an Activity in another app is set to do the same? To try this out, create a new project called IntentDemo2 and create a new page inside IntentDemo2 called HomeActivity:

<activity android:name=".HomeActivity">
    <intent-filter>
        <action android:name="com.cyy.jump" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
 </activity>
Copy the code

Run the project again and see what happens:

As you can see, the IntentDemo2 option is displayed in the dialog box. Click to jump to the IntentDemo2 HomeActivity page.

ACTION_SEARCH = ACTION_SEARCH = ACTION_SEARCH = ACTION_SEARCH

Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEARCH);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
Copy the code

Run the project to see the effect:

At this time, the system will pop up a list of all applications that meet the ACTION_SEARCH requirements for the user to select.

Some actions defined by the system:

Action meaning
ACTION_MAIN Android program entry
ACTION_VIEW Display specified data
ACTION_EDIT Edit specified data
ACTION_DIAL Display dial panel
ACTION_CALL Call the number in Data directly
ACTION_ANSWER Answering calls
ACTION_SEND Sending data to others (e.g., MMS /email)

The above is just a brief list. For more information, please refer to the official document: Intent

  1. Category: the CategoryThe Category attribute adds additional Category information to the Action. Commonly used asCATEGORY_DEFAULT, which represents the default execution mode in the Android system. It is executed according to the ordinary Activity execution mode. In such asCATEGORY_LAUNCHER, sets this component to the highest priority among the current application initiators, usually with the program entry actionACTION_MAIN Use together:
<activity android:name=".MainActivity">
	<intent-filter>
		<action android:name="android.intent.action.MAIN" />

		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
Copy the code
  1. Data & Type: Data & TypeThe Data attribute typically provides Data for the Action attribute to operate on, such as a specified phone number to call, a specified phone number to send an SMS, and the contents of the SMS. The value of the Data attribute is aUriObject in the following format:
schema://host:port/path
Copy the code
  • Schema agreement
  • Host the host
  • Port to port
  • The path path

Such as: http://www.baidu.com:8080/a.jpg

Several Data property constants built into the system

agreement meaning
tel: Number data format followed by phone number
mailto: Message data format followed by message recipient address
smsto: SMS data format followed by the receiving phone number
file:/// File data format, followed by file path. Note that it must be three slashes ///
content:// Content data format, followed by the content to be read. Contentprovider-specific format

Here are some examples:

Example 1: Open a web page

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
Copy the code

Example 2: Make a phone call

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("tel:18755555555"));
startActivity(intent);
Copy the code

TypeAttribute is used to specify the corresponding Uri specified by Data MIMEType, usually used to call the system App, such as the implementation to view a file (text, picture, audio, video, etc.), by specifying the MIME type of the file, you can let the system know which program to open the file.

To setData, call setData(), and to setType, call setType. Note that the two methods cannot be set at the same time, they will be overridden. If you want to set both Data and Type, call setDataAndType. We can look at the source code:

/**
     * Set the data this intent is operating on.  This method automatically
     * clears any type that was previously set by {@link #setType} or
     * {@link #setTypeAndNormalize}.
     *
     * <p><em>Note: scheme matching in the Android framework is
     * case-sensitive, unlike the formal RFC. As a result,
     * you should always write your Uri with a lower case scheme,
     * or use {@link Uri#normalizeScheme} or
     * {@link #setDataAndNormalize}
     * to ensure that the scheme is converted to lower case.</em>
     *
     * @param data The Uri of the data this intent is now targeting.
     *
     * @return Returns the same Intent object, for chaining multiple calls
     * into a single statement.
     *
     * @see #getData
     * @see #setDataAndNormalize
     * @see android.net.Uri#normalizeScheme()
     */
    public @NonNull Intent setData(@Nullable Uri data) {
        mData = data;
        mType = null;
        return this; }.../**
     * Set an explicit MIME data type.
     *
     * <p>This is used to create intents that only specify a type and not data,
     * for example to indicate the type of data to return.
     *
     * <p>This method automatically clears any data that was
     * previously set (for example by {@link #setData}).
     *
     * <p><em>Note: MIME type matching in the Android framework is
     * case-sensitive, unlike formal RFC MIME types.  As a result,
     * you should always write your MIME types with lower case letters,
     * or use {@link #normalizeMimeType} or {@link #setTypeAndNormalize}
     * to ensure that it is converted to lower case.</em>
     *
     * @param type The MIME type of the data being handled by this intent.
     *
     * @return Returns the same Intent object, for chaining multiple calls
     * into a single statement.
     *
     * @see #getType
     * @see #setTypeAndNormalize
     * @see #setDataAndType
     * @see #normalizeMimeType
     */
    public @NonNull Intent setType(@Nullable String type) {
        mData = null;
        mType = type;
        return this; }.../**
     * (Usually optional) Set the data for the intent along with an explicit
     * MIME data type.  This method should very rarely be used -- it allows you
     * to override the MIME type that would ordinarily be inferred from the
     * data with your own type given here.
     *
     * <p><em>Note: MIME type and Uri scheme matching in the
     * Android framework is case-sensitive, unlike the formal RFC definitions.
     * As a result, you should always write these elements with lower case letters,
     * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalizeScheme} or
     * {@link #setDataAndTypeAndNormalize}
     * to ensure that they are converted to lower case.</em>
     *
     * @param data The Uri of the data this intent is now targeting.
     * @param type The MIME type of the data being handled by this intent.
     *
     * @return Returns the same Intent object, for chaining multiple calls
     * into a single statement.
     *
     * @see #setType
     * @see #setData
     * @see #normalizeMimeType
     * @see android.net.Uri#normalizeScheme
     * @see #setDataAndTypeAndNormalize
     */
    public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
        mData = data;
        mType = type;
        return this;
    }
Copy the code

Here is an example of how to use Data and Type:

See a picture in the mobile phone, address is: storage/emulated / 0 / DCIM/IMG_201910297_162012_328. JPG

File file = new File("storage/emulated/0/DCIM/IMG_201910297_162012_328.jpg");
Intent intent = new Intent();
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "image/jpeg");
startActivity(intent);
Copy the code

Run to see effect:

Successful visit!

Common MIME types:

The file format The corresponding MIME type
.bmp image/bmp
.gif image/gif
.png image/png
.tif .tiff image/tiff
.jpe .jpeg .jpg image/jpeg
.txt text/plain
.xml text/xml
.html text/html
.css text/css
.js text/javascript
.mht .mhtml message/rfc822
.doc application/msword
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.rtf application/rtf
.xls application/vnd.ms-excel application/x-excel
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.ppt application/vnd.ms-powerpoint
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
.pps application/vnd.ms-powerpoint
.ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow
.pdf application/pdf
.swf application/x-shockwave-flash
.dll application/x-msdownload
.exe application/octet-stream
.msi application/octet-stream
.chm application/octet-stream
.cab application/octet-stream
.ocx application/octet-stream
.rar application/octet-stream
.tar application/x-tar
.tgz application/x-compressed
.zip application/x-zip-compressed
.z application/x-compress
.wav audio/wav
.wma audio/x-ms-wma
.wmv video/x-ms-wmv
.mp3 .mp2 .mpe .mpeg .mpg audio/mpeg
.rm application/vnd.rn-realmedia
.mid .midi .rmi audio/mid
  1. Extra, ExtraProperty is used to add additional information, and its property value is a Bundle object that stores data as key-value pairs. It is less used in implicit Intents and is mainly used to display the data passed by the Intent. Here is a simple demonstration:
//MainActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("name"."My name is Android");
startActivity(intent);
Copy the code
//SecondActivity
Intent intent = getIntent();
if(intent ! =null) {
	String name = intent.getStringExtra("name");
	textView.setText(name);
}
Copy the code

6. Flags: tagUsually used to dynamically configure the startup mode of an Activity. In most cases, you don’t need to set Flags, so you’ll be able to understand them. Here are some common ones:

FLAG_ACTIVITY_NEW_TASK: This bit specifies the “singleTask” launch mode for the Activity. It does the same thing as specifying the launch mode in the manifest file.

FLAG_ACTIVITY_SINGLE_TOP: This bit specifies the “singleTop” startup mode for the Activity. It does the same thing as specifying the “singleTop” startup mode in the manifest.

FLAG_ACTIVITY_CLEAR_TOP: The Activity with this flag bit, when it starts, all activities above it in the same task stack are off the stack.

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS: An Activity with this flag does not appear in the list of historical activities. It is equal to the Activity specified in the manifest file attributes, android: excludeFromRecents = “true”.

Intents are implicit

In the previous section, we introduced the seven properties of an Intent, and we also showed you how to define an explicit Intent and an implicit Intent. It’s easier to use an explicit Intent, so we won’t go into much more detail. Now we’ll give you an analysis of implicit Intent.

(1) Receiving implicit Intents

To declare which implicit Intents a component can receive, declare one or more Intent filters for the component using the

element in the manifest file. The main properties set in each < Intent-Filter > are

,
, and

. When an implicit Intent matches one of the < Intent-filter > s, the Intent is passed to the application component (an explicit Intent is always passed to its target component, regardless of what

is declared by the target component).

An application component should declare a separate < Intent-filter > for each unique job that it can execute. For example, an Activity in an image library application might have two < Intent-Filters >, one for viewing an image and one for editing an image. When an Activity starts, it checks for the Intent and determines the specific behavior (for example, whether to display an edit image control) based on the information in the Intent.

Special note: In < Intent-filter >, you must set a default

: < category android: name = “android. Intent. The category. The DEFAULT” / >, or component will not receive an implicit intent.

If we don’t want our component to be started by another application and only want it to be used in this application, then we don’t want to declare an < Intent-filter > in the listing and set the component exported property to false.

< Intent-filter >

<activity android:name="MainActivity">
    <! -- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity">
    <! -- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <! -- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>
Copy the code

The first Activity, MainActivity, is the main entry for the application:

  • ACTION_MAINOperations indicate that this is the primary entry and does not require any Intent data.
  • CATEGORY_LAUNCHERThe category indicates that the icon for this Activity should go into the system’s application initiator. If an element does not specify an icon using icon, the system uses the icon in the element.

These two elements must be paired for the Activity to appear in the application initiator.

The second Activity, ShareActivity, is designed to make it easy to share text and media content. Although you can enter the Activity from MainActivity, you can also enter ShareActivity directly from another application that issued an implicit Intent that matches one of the two Intent filters.

The MIME type application/VND. Google. Panorama360 + JPG is a special data type specified panoramic photos

(2) What to note when declaring an implicit Intent

Note 1: What happens when no application responds to the implicit Intent we pass with startActivity()?

We define an Action, but don’t let any Activity receive the Intent:

Intent intent = new Intent();
intent.setAction("com.cyy.send");
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
Copy the code

After running, the application crashed with the following Error message:

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.cyy.send cat=[android.intent.category.DEFAULT] }

To avoid this, we need to call resolveActivity() to verify that any Activity can receive the Intent before calling startActivity(). Here’s how:

Intent intent = new Intent();
intent.setAction("com.cyy.send");
intent.addCategory(Intent.CATEGORY_DEFAULT);

if(intent.resolveActivity(getPackageManager()) ! =null) {
	startActivity(intent);
}
Copy the code

If the result of the resolveActivity is non-empty, then at least one application can handle the Intent, and it’s safe to call startActivity().

Note 2: When multiple apps are available to respond to our implicit Activity, a selection box will pop up to let the user select the app to open. The user can also select the app to remember to open, so that the selection box will not pop up again. What if I want it to pop up every time so the user doesn’t remember? We can create an Intent using createChooser().

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));

String title = "Please select a browser";
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(intent, title);
if(intent.resolveActivity(getPackageManager()) ! =null) {
	startActivity(chooser);
}
Copy the code

The running effect is as follows:

References:

  1. Intents and Intent filters
  2. Exploring the Art of Android Development by Ren Yugang