写在前面

Android中,意图(Intent)是一个消息传递对象,活动、服务和广播接收器之间调用和消息传递都是通过意图实现的。意图的三个基本的用途为:启动Activity、启动Service和传递广播(Broadcast)。之前讲活动(Activity),不同活动之间切换的时候已经用到过Intent了。

Intent分类

Intent分为两种类型:显式意图和隐式意图。显式意图通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理 Intent 的应用,一般在自己编写的应用中使用较多,当需要启动其它Activity或者Service的时候,你可能需要指定它们的名称。而隐式意图则不指定特定的组件,而是声明相关的操作,由系统通过意图过滤器(Intent Filter)来寻找匹配的组件加以启动,如果同时有多个组件匹配,系统会弹出选择框让用户选择。
意图过滤器(IntentFilter)是Manifest文件中的表达式,它指定了组件接收Intent的类型,只有当满足意图过滤器的意图来调用组件的时候,组件才会响应。
对于显示意图而言,指定组件名称的方法有很多:可以使用 setComponent()、setClass()、setClassName(),或 Intent 构造函数设置组件名称。使用构造函数如下:

Intent it = new Intent(MainActivety.this, AnotherActivity.class);
startActivity(it);

或者调用上述的方法来设置组件名称:

Intent it = new Intent();
it.setClass(MainActivity.this, AnotherActivity.class);
startActivity(it);

这就是两种方法构建了一个指定了组件名称的意图对象,用于启动组件。对于隐式意图,涉及到和IntentFilter的匹配问题,先来谈一下意图的几个重要属性。

意图属性

为了实现隐式意图和组件之间的匹配,意图有三个属性可以设置:动作(Action)、数据(Data)和类别(Category)

动作(Action)

Action是用来指定要执行的操作的通用字符串。在Android系统中,提供了一些通用的Action字符串如:ACTION_CALL、ACTION_VIEW、ACTION_SEND等,可以通过setAction()方法来设置意图的Action。

Intent it = new Intent();
it.setAction(ACTION_SEND);
startActivity(it);

当然还要在Manifest文件中相关的Activity处指定意图过滤器:

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

这样通过隐式意图的方法同样可以调用Activity。另外,除了Android系统定义的一些Action字符串,用户还可自己定义,只要在Manifest文件中定义即可,构造意图的时候用相应的字符串匹配。

数据(Data)

数据(Data)用来指定目标组件需要的数据,它由指定数据的URI和数据的MIME类型两部分组成。
URI是一种资源标识符,可以用来指定(定位)资源,它的格式为:< scheme >://< host >:< port >/< path >;例如:https://www.baidu.com/index.html 这样唯一的指定了资源。
MIME类型是数据的资源类型,例如图片资源、声音资源等,资源需要匹配,因为显示图片的组件不能够用来播放音乐。类型举例:text/html、image/png等。
组件意图过滤器在Manifest文件中格式如下:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/MP3" android:scheme="http" ... />
    ...
</intent-filter>

指定了资源的类型为video/mpeg、audio/mpeg,也指定了URI的部分内容。
为使上面声明的意图过滤器能够响应,需要构造意图:

Intent it = new Intent();
Uri playUri = Uri.parse("http://www.baidu.com/mp3/test.mp3");
it.setDataAndType(palyUir, "audio/MP3");
startActivity(it);

这里设置了意图的URI和MIME类型;这里要注意的一点是:在隐式意图中,单独设置MIME类型使用setType()方法,单独设置URI使用setData()方法,同时设置两者需要调用setDataAndType()方法,而不能同时使用setType()和setData(),这样会发生设置的覆盖。

类别(Category)

类别是一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,常用的类别有以下两种:
(1)

<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN" />

上面LAUNCHER类别和action标签一起使用表示该Activity是启动的活动。
(2)

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

如果自己定义的activity需要被隐式意图启动的话,必须指定一个category,默认的话就指定为android.intent.category.DEFAULT;不加这个指定的类别,隐式意图匹配测试就会失败。
当然也可以和Action一样自己定义类别,不再赘述。

Android内置意图

这里举例说明一些常见的Android内置意图,内置的意图可以用来打开浏览器、地图等等。

布局如下:

android 意图申明 android显示意图_隐式意图


MainActivity.java的代码如下:

package com.beihang.test;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    public static final String TAG = "MainActivity";
    private Button btn1, btn2, btn3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initUI();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_1:
                Uri uri = Uri.parse("https://www.baidu.com/");
                Intent it = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(it);
                break;
            case R.id.bt_2:
                Uri uri1 = Uri.parse("tel:1008611");
                Intent it1 = new Intent(Intent.ACTION_VIEW, uri1);
                startActivity(it1);
                break;
            case R.id.bt_3:
                Uri uri2 = Uri.parse("geo:39.9,116.4");
                Intent it2 = new Intent(Intent.ACTION_VIEW, uri2);
                startActivity(it2);
                break;
        }
    }

    private void initUI() {
        btn1 = findViewById(R.id.bt_1);
        btn2 = findViewById(R.id.bt_2);
        btn3 = findViewById(R.id.bt_3);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        btn3.setOnClickListener(this);
    }

}

逻辑非常简单,定义了三个按钮,分别设置点击响应。新建对象,设置URI和Action,对于电话、浏览器、地图各不相同,最后调用startActivity()方法启动相应的组件。使用Android Studio里面模拟器的话,GoogleMap无法访问(由于虚拟手机不能科学上网的原因),这里直接用我自己的手机调试效果,分别点击不同按钮可以的到截图如下:

android 意图申明 android显示意图_android_02

android 意图申明 android显示意图_Android_03

android 意图申明 android显示意图_android_04


可以看到,点击不同的按钮,调用了我们期望的系统组件,由于我的手机没有安装GoogleMap,所以给我调用的高德地图,这说明高德地图Activity的Intentfilter的内容也是匹配我们的Intent的。更多的Android系统组件的调用可以查看官方的手册。

总结

本篇介绍了Android中一个十分重要的对象Intent,当然由于初学的缘故,没有很深入的去介绍,只是提到了基本用法。包括了意图的分类,意图的三个属性:Action、Data、Category,最后还举例说明了Android系统内部一些组件的调用。参考文献有:官网:https://developer.android.google.cn/guide/components/intents-filters?hl=zh_cn#java以及《移动操作系统原理与实践》关东升。
水平所限,文中可能存在小错误。