1.3 Intent 和 Intent 过滤器 - TomeOkin/Learning-Notes GitHub Wiki

Intent 和 Intent 过滤器

Intent 的组成

Intent 由 data、action、category、component name(可选)、extra(可选)、flags 等组成,每一部分都可以自定义。

  • data:由 mimeType 和 URI 组成,mimeType 指明媒体类型,比如 text/*image/jpeg。URI 由 scheme、host、port 和 path 属性构成,路径可以使用正则表达式:
    <scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
    比如:https://www.google.com:443 或者 content://com.example.project:200/folder/subfolder/etc

小技巧:android.resource://包名/资源Id 可以访问 resource 文件夹里的内容

  • action:指定要执行的操作类型,比如 ACTION_VIEWACTION_EDITACTION_SEND,也可以自定义。

  • category:指定要操作的类别,比如要在启动器显示的意图类别就会设置为 android.intent.category.LAUNCHER(如果要支持多窗口,还会加上 android.intent.category.MULTIWINDOW_LAUNCHER)。如果要默认匹配,则会加上 android.intent.category.DEFAULT

  • component name:用在显式意图中,指明处理该意图的 Activity、Service 或 BroadcastReceiver。

  • extra:可以通过 putExtra() 方法往 Intent 里添加内容,或者先添加到 Bundle 里,再添加到 Intent 中。

  • flags:对应与启动模式里的 flags。

Intent Filter

意图的书写方式有两种,一种是写在 AndroidManifest.xml 的意图过滤器中,表示的是支持处理或想要处理该意图;另一种是直接在代码里使用,以隐式意图的方式启动支持处理该意图的 Activity、Service 或 BroadcastReceiver 或显式指定处理该意图的 Activity、Service 或 BroadcastReceiver。

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

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </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>
</activity>

在代码里使用时,如果要同时设置 Uri 和 mimeType,要使用的是 setDataAndType()setData() 会设置 Uri 并清除 mimeType,同样,setType() 会设置 mimeType 并清除 Uri。

// 隐式意图
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_SUBJECT, R.string.share_subject);
i.putExtra(Intent.EXTRA_TEXT, theMessage);

// 显示意图
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

意图的匹配规则

匹配过程中,如果存在多个过滤器,只需通过其中一个过滤器即可。匹配过滤器时,action 必须至少匹配一项才能通过,category 必须全部都匹配才能通过,data 也只需匹配通过其中一项即可。

系统默认在启动隐式意图时,会自动为 category 加上 CATEGORY_DEFAULT,故一般情况下,定义 category 时,为了保证能匹配通过,一般 category 都会含有 android.intent.category.DEFAULT,或者再多一项用于准确匹配。

如果找不到匹配的组件,那么执行 start 操作时,会抛出异常。所以,一般情况下,会先判断是否有支持的组件。
PackageManager 中提供了一系列 query 方法用于查询是否支持的组件,还有 resolve 系列方法用于查询最合适的意图(也可以使用 Intent 的 resolve 系列方法)。如果查询到的意图很多,那么还可以使用启动选择器供用户选择合适的组件。

Intent startupIntent = new Intent(Intent.ACTION_MAIN);
startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);

PackageManager pm = getActivity().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(startupIntent, 0);
Collections.sort(activities, new Comparator<ResolveInfo>() {
    public int compare(ResolveInfo a, ResolveInfo b) {
        PackageManager pm = getActivity().getPackageManager();
        return String.CASE_INSENSITIVE_ORDER.compare(
                a.loadLabel(pm).toString(),
                b.loadLabel(pm).toString());
    }
});
final Intent pickContact = new Intent(Intent.ACTION_PICK, 
	ContactsContract.Contacts.CONTENT_URI);
PackageManager packageManager = getActivity().getPackageManager();
if (packageManager.resolveActivity(pickContact, 
	PackageManager.MATCH_DEFAULT_ONLY) == null) {
    // do something
}

特别地,resolveActivity 的第二个参数 flags 设置为 PackageManager.MATCH_DEFAULT_ONLY,指明只检索那些有设置 android.intent.category.DEFAULT 的应用,至于 queryIntentActivities 为什么第二个参数 flags 为什么使用的是 0,原因是作为应用启动 Activity,只需要具备 action 为 android.intent.action.MAIN 且 category 为 android.intent.category.LAUNCHER" 即可启动。

PendingIntent

一个封装的 Intent,主要提供给外部应用执行应用自身意图时使用:

  • 当用户操作通知时,NotificationManager 执行该意图。
  • 用户操作 App Widget 时,桌面启动器执行该意图。
  • 定时任务,AlarmManager 在对应的时间执行该意图。

参考

Intents and Intent Filters 官方文档
《Android 开发艺术探索》
《The Busy Coders Guide to Android Development》
《Android Programming:The Big Nerd Ranch Guide》 最下方两个小 demo

⚠️ **GitHub.com Fallback** ⚠️