Android内部分享[4]——Intent 和 Intent 过滤器

概述

前面我们已经对 Android 中的界面跳转和本地存储有了一定的认识,接下来我们来详细研究一下 Android 中的意图(Intent),理解它有助于我们后续理解系统广播和服务。

我们先来回顾一下前面的界面跳转代码:

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

可以看到 Intent 就像一座桥梁,来实现从一个界面到另一个界面的切换,事实上上面的是显式的 Intent,我们很明确我们即将要跳转到的页面,还有一种意图是不可知不确定的,或者说在绑定意图的时候不是固定的某个具体的 Activity, 例如:

Intent intent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
startActivity(intent);

这个时候我们的 Intent (意图)可能指定的不是一个界面了,有可能是多个界面,只要我们的 Activity 含 ACTION_SEND 操作并携带 text/plain 数据就会被调起。如果只有一个应用能够处理,则该应用将立即打开并为其提供 Intent。 如果多个 Activity 接受 Intent,则系统将显示一个对话框,使用户能够选取要使用的应用。

多个意图响应

携带的数据

Intent 对象携带了 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

组件名称:要启动的组件名称(这里的组件不仅仅指 Activity, 还包括 Service)。

这虽然在 Intent 构造函数中是一个可选项,但是在显式意图中是一个很重要的信息,如果没有这个组件名称就是隐式的意图。

Intent intent = new Intent();
intent.setClassName(this, SecondActivity.class.getName());
startActivity(intent);

也可以使用上面这种 setClassName 来设置组件名称, 第一个参数可以是 context 也可以是 packageName, 第二个参数是组件的全路径字符串, 和下方代码是等价的:

intent.setClassName(this, "com.dlc.helloword.activity.SecondActivity");

或者:

intent.setClassName(getPackageName(), "com.dlc.helloword.activity.SecondActivity");

实际上在 Intent 中表示组件名称的对象是 ComponentName, 上面的所有写法只不过是提供给我们的简写方式,使用该对象设置组件名称如下:

ComponentName 构造函数

Intent intent = new Intent();
ComponentName componentName = new ComponentName(getPackageName(),
        "com.dlc.helloword.activity.SecondActivity");
intent.setComponent(componentName);
startActivity(intent);

同理,根据构造函数我们可以传递 context 和 Class 对象。

ACTION : 指定要执行的通用操作(例如,查看选取)的字符串。

你可以在 AndroidManifest.xml 文件中定义自己的 ACTION, 就像我们的入口 Activity 一样。

<activity android:name=".activity.MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

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

上面入口函数定义的 ACTION 是 android.intent.action.MAIN, 但是大多数情况下我们会使用系统定义的 ACTION 常量, 在 Intent 类中定义了很多 ACTION 常量如下:

Intent 中的 ACTION 常量

例如 ACTION_VIEW 向用户显示信息,例如照片, ACTION_SEND 共享数据等,在 Intent 中对 ACTION 进行了分类:

标准的 Activity ACTIONS:

  • ACTION_MAIN:入口Activity 不期望接收数据。
  • ACTION_VIEW:最常见的操作,向用户显示数据。
  • ACTION_ATTACH_DATA
  • ACTION_EDIT
  • ACTION_PICK
  • ACTION_CHOOSER
  • ACTION_GET_CONTENT
  • ACTION_DIAL
  • ACTION_CALL
  • ACTION_SEND
  • ACTION_SENDTO
  • ACTION_ANSWER
  • ACTION_INSERT
  • ACTION_DELETE
  • ACTION_RUN
  • ACTION_SYNC
  • ACTION_PICK_ACTIVITY
  • ACTION_SEARCH
  • ACTION_WEB_SEARCH
  • ACTION_FACTORY_TEST

标准的 Broadcast(广播)ACTIONS:

  • ACTION_TIME_TICK
  • ACTION_TIME_CHANGED
  • ACTION_TIMEZONE_CHANGED
  • ACTION_BOOT_COMPLETED
  • ACTION_PACKAGE_ADDED
  • ACTION_PACKAGE_CHANGED
  • ACTION_PACKAGE_REMOVED
  • ACTION_PACKAGE_RESTARTED
  • ACTION_PACKAGE_DATA_CLEARED
  • ACTION_PACKAGES_SUSPENDED
  • ACTION_PACKAGES_UNSUSPENDED
  • ACTION_UID_REMOVED
  • ACTION_BATTERY_CHANGED
  • ACTION_POWER_CONNECTED
  • ACTION_POWER_DISCONNECTED
  • ACTION_SHUTDOWN

例如我们打开浏览器访问网站,或者打开键盘拨号界面:

public void testIntent(View view){
    Uri uri = Uri.parse("https://dp2px.com");         //浏览器
    //Uri uri = Uri.parse("tel:1232333");              //拨号程序
    //Uri uri= Uri.parse("geo:39.899533,116.036476");  //打开地图定位
    Intent intent  = new Intent(Intent.ACTION_VIEW, uri);  //Intent.ACTION_VIEW不带引号
    startActivity(intent);
}

Data:引用待操作数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作决定。

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://dp2px.com"));
startActivity(intent);

如果要设置 MIME 类型,调用 setType() 方法。

Category:一个包含应处理 Intent 组件类型的附加信息的字符串。

还记得上面的入口 Activity 注册表中的 category 吗?

<activity android:name=".activity.MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

这个 category 表示此 Activity 应该显示在 launcher 的顶层。当然我们也可以自己去设定 category 去分门别类,这个就有点像给 Activity 添加标签。通过 addCategory() 方法来启动该标签的 Activity。

过滤器

上面的 action, category, data 都需要在 <intent-filter> 中添加,而且可以添加多个,例如:

<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>

这样我们就可以从各个层面去过滤, 当我们调用 Intent 意图去跳转的时候,会坚持是不是 SEND 和 SEND_MULTIPLE 的 action, 数据是不是 image 或者 video 类型,如果不是则不会调起该 Activity.

PendingIntent

我们上面的 Intent 使用 startActivity() 方法可以理解执行意图,而有时候我们不需要立即执行,而是要等待一个条件去触发执行,这个时候就需要使用 PendingIntent.

PendingIntent 有三个静态方法,分别获得对应组件的 Intent 对象:

  • getActivity(Context, int, Intent, int):跳转到一个activity组件、
  • getBroadcast(Context, int, Intent, int):打开一个广播组件
  • getService(Context, int, Intent, int):打开一个服务组件。

    private void showNotify(){   
    Notification notice=new Notification();   
    notice.icon=R.drawable.icon;   
    notice.tickerText="您有一条新的信息";   
    notice.defaults=Notification.DEFAULT_SOUND;   
    notice.when=10L;   
    //即将跳转页面,还没跳转   
    notice.setLatestEventInfo(this, "通知", "开会啦", 
            PendingIntent.getActivity(this, 0, new Intent(this,Activity2.class), 0));
    NotificationManager manager=(NotificationManager)getSystemService(this.NOTIFICATION_SERVICE);   
    manager.notify(0,notice);   
    }
    

例如上面是通知 Notification 点击跳转的一个例子,通常还常常用于短消息 SmsManager 的发送和警报器 AlarmManager 的执行等。