Intent和Intent过滤器

概述

Intent是用于和其他应用组件通信的消息对象,

通常有三种用途:启动Activity、启动Service、发送Broadcast

Intent类型

  • 显式Intent
  • 隐式Intent

创建Intent

  • Component name

    • setComponent()
    • setClass()
    • setClassName()
  • Action

    • ACTION_VIEW
    • ACTION_SEND
  • Data

    Uri对象、指定数据的MIME类型

    • setDataAndType()
  • Category

    • CATEGORY_BROWSABLE
    • CATEGORY_LAUNCHER
  • Extras

    Bundle键值对

  • Flags

    指示如何启动Activity,以及启动后它所属的任务栈

示例: App选择器
val sendIntent = Intent(Intent.ACTION_SEND)
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
val title: String = resources.getString(R.string.chooser_title)
// Create intent to show the chooser dialog
val chooser: Intent = Intent.createChooser(sendIntent, title)

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(chooser)
}

接收隐式Intent

清单文件中定义<intent-filter>

- <action>
- <data>
- <category>
<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>

PendingIntent

使用场景:

- Notification
- App Widget
- AlarmManager

创建:

- PendingIntent.getActivity()
- PendingIntent.getService()
- PendingIntent.getBroadcast()

<font color="red">解析Intent</font>

  • action

    Intent中指定的action必须与intent-filter中列出的action一项匹配。

    若intent-filter中不包含action,匹配永远不会成功。

    若Intent中未指定action,而只要intent-filter至少包含一个action就会默认匹配成功。

  • category

    Intent中的每个category都必须与intent-filter中列出的category匹配。

    即Intent中指定的category必须是intent-filter中category的子集。

    若Intent不包含category,则匹配成功。

  • data

    1. 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
    2. 对于包含 URI 但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
    3. 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型但不含 URI 的 Intent 才会通过测试。
    4. 如果过滤器只是列出 MIME 类型,则假定组件支持 content:file: 数据。

Intent匹配

PackageManager 提供一整套 query...() 方法来返回所有能够接受特定 Intent 的组件。

此外,还会提供一系列类似的 resolve...() 方法来确定响应 Intent 的最佳组件。

常见Intent

闹钟

  • Action:ACTION_SET_ALARM
  • Extras
    • EXTRA_HOUR
    • EXTRA_MINUTES
    • EXTRA_MESSAGE
    • EXTRA_DAYS
    • EXTRA_RINGTONE
    • EXTRA_VIBRATE
    • EXTRA_SKIP_UI
//SET_ALARM permission 
fun createAlarm(message: String, hour: Int, minutes: Int) {
    val intent = Intent(AlarmClock.ACTION_SET_ALARM).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_HOUR, hour)
        putExtra(AlarmClock.EXTRA_MINUTES, minutes)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

定时器

  • Action:ACTION_SET_TIMER
  • Extras:
    • EXTRA_LENGTH
    • EXTRA_MESSAGE
    • EXTRA_SKIP_UI
//SET_ALARM permission
fun startTimer(message: String, seconds: Int) {
    val intent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

显示所有闹钟

  • Action:ACTION_SHOW_ALARMS

日历

  • Action:ACTION_INSERT
  • Data URI:Events.CONTENT_URI
  • MIME Type:“vnd.android.cursor.dir/event”
  • Extra:
    • EXTRA_EVENT_ALL_DAY
    • EXTRA_EVENT_BEGIN_TIME
    • EXTRA_EVENT_END_TIME
    • TITLE
    • DESCRIPTION
    • EVENT_LOCATION
    • EXTRA_EMAIL
fun addEvent(title: String, location: String, begin: Long, end: Long) {
    val intent = Intent(Intent.ACTION_INSERT).apply {
        data = Events.CONTENT_URI
        putExtra(Events.TITLE, title)
        putExtra(Events.EVENT_LOCATION, location)
        putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin)
        putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

相机

拍摄照片或视频

相对于相机应用,此界面只要拍摄功能,无相册

  • Action:ACTION_IMAGE_CAPTURE or ACTION_VIDEO_CAPTURE
  • Extras:EXTRA_OUTPUT
const val REQUEST_IMAGE_CAPTURE = 1
val locationForPhotos: Uri = ...

fun capturePhoto(targetFilename: String) {
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
        putExtra(MediaStore.EXTRA_OUTPUT, Uri.withAppendedPath(locationForPhotos, targetFilename))
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
        val thumbnail: Bitmap = data.getParcelableExtra("data")
        // Do other work with full size photo saved in locationForPhotos
        ...
    }
}

For more information about how to use this intent to capture a photo, including how to create an appropriate Uri for the output location, read Taking Photos Simply or Taking Videos Simply.

启动相机应用
  • INTENT_ACTION_STILL_IMAGE_CAMERA
  • INTENT_ACTION_VIDEO_CAMERA

联系人

选择联系人
  • Action:ACTION_PICK
  • MIME Type:Contacts.CONTENT_TYPE
选择特定联系人数据
  • Action:ACTION_PICK
  • MIME Type:
    • CommonDataKinds.Phone.CONTNET_TYPE
    • CommonDataKinds.Email.CONTNET_TYPE
const val REQUEST_SELECT_PHONE_NUMBER = 1

fun selectContact() {
    // Start an activity for the user to pick a phone number from contacts
    val intent = Intent(Intent.ACTION_PICK).apply {
        type = CommonDataKinds.Phone.CONTENT_TYPE   
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == Activity.RESULT_OK) {
        // Get the URI and query the content provider for the phone number
        val contactUri: Uri = data.data
        val projection: Array<String> = arrayOf(CommonDataKinds.Phone.NUMBER)
        contentResolver.query(contactUri, projection, null, null, null).use { cursor ->
            // If the cursor returned is valid, get the phone number
            if (cursor.moveToFirst()) {
                val numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER)
                val number = cursor.getString(numberIndex)
                // Do something with the phone number
                ...
            }
        }
    }
}
查看联系人
  • Action:ACTION_VIEW
  • Data URI:content:<URI>
fun viewContact(contactUri: Uri) {
    val intent = Intent(Intent.ACTION_VIEW, contactUri)
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
编辑现有联系人
  • Action:ACTION_EDIT
  • Data URI:content:<URI>
  • MIME Type:该类型是从联系人 URI 推断得出
  • Extra:ContactsContract.Intents.Insert
fun editContact(contactUri: Uri, email: String) {
    val intent = Intent(Intent.ACTION_EDIT).apply {
        data = contactUri
        putExtra(ContactsContract.Intents.Insert.EMAIL, email)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
插入联系人
  • Action:ACTION_INSERT
  • MIME Type:Contacts.CONTENT_TYPE
  • Extra:ContactsContract.Intents.Insert
fun insertContact(name: String, email: String) {
    val intent = Intent(Intent.ACTION_INSERT).apply {
        type = ContactsContract.Contacts.CONTENT_TYPE
        putExtra(ContactsContract.Intents.Insert.NAME, name)
        putExtra(ContactsContract.Intents.Insert.EMAIL, email)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

电子邮件

文件存储

检索特定类型的文件
  • Action:ACTION_GET_CONTENT
  • MIME Type:文件对应类型
  • Extra:
    • EXTRA_ALLOW_MULTIPLE
    • EXTRA_LOCAL_ONLY
  • Category(可选):CATEGORY_OPENABLE
//获取照片
const val REQUEST_IMAGE_GET = 1

fun selectImage() {
    val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
        type = "image/*"
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_GET)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_IMAGE_GET && resultCode == Activity.RESULT_OK) {
        val thumbnail: Bitmap = data.getParcelableExtra("data")
        val fullPhotoUri: Uri = data.data
        // Do work with photo saved at fullPhotoUri
        ...
    }
}
打开特定类型的文件
  • Action:ACTION_OPEN_DOCUMENT or ACTION_CREATE_DOCUMENT

  • MIME Type:文件对应类型

  • Extra:

    • EXTRA_MIME_TYPES
    • EXTRA_ALLOW_MULTIPLE
    • EXTRA_TITLE
    • EXTRA_LOCAL_ONLY
  • Category:CATEGORY_OPENABLE

//获取照片2
const val REQUEST_IMAGE_OPEN = 1

fun selectImage2() {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        type = "image/*"
        addCategory(Intent.CATEGORY_OPENABLE)
    }
    // Only the system receives the ACTION_OPEN_DOCUMENT, so no need to test.
    startActivityForResult(intent, REQUEST_IMAGE_OPEN)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_IMAGE_OPEN && resultCode == Activity.RESULT_OK) {
        val fullPhotoUri: Uri = data.data
        // Do work with full size photo saved at fullPhotoUri
        ...
    }
}

地图

  • Action:ACTION_VIEW

  • Data URI:

    • geo:latitude,longitude

      显示给定经纬度地图

    • geo:latitude,longitude?z=zoom

      按照特定缩放级别显示(1-23,1为全球地图,23为最高精度缩放)

    • geo:0,0?q=lat,lng(label)

      显示给定经纬度处带字符串标签的地图

    • geo:0,0?q=my+street+address

      显示“我的街道”位置

fun showMap(geoLocation: Uri) {
    val intent = Intent(Intent.ACTION_VIEW).apply {
        data = geoLocation
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

音乐或视频

播放媒体文件
  • Action:ACTION_VIEW
  • Data URI:
    • file:<URI>
    • content:<URI>
    • http:<URI>
  • MIME Type:
    • "audio/*"
    • "application/ogg"
    • "application/x-ogg"
    • "application/itunes"
fun playMedia(file: Uri) {
    val intent = Intent(Intent.ACTION_VIEW).apply {
        data = file
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
基于搜索播放音乐
  • Action:INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
  • Extra:MediaStore.EXTRA_MEDIA_FOCUS
fun playSearchArtist(artist: String) {
    val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH).apply {
        putExtra(MediaStore.EXTRA_MEDIA_FOCUS, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)
        putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist)
        putExtra(SearchManager.QUERY, artist)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

新笔记

  • Action:ACTION_CREATE_NOTE
  • MIME Type:PLAIN_TEXT_TYPE
  • Extra:
    • EXTRA_NAME
    • EXTRA_TEXT
fun createNote(subject: String, text: String) {
    val intent = Intent(NoteIntents.ACTION_CREATE_NOTE).apply {
        putExtra(NoteIntents.EXTRA_NAME, subject)
        putExtra(NoteIntents.EXTRA_TEXT, text)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

电话

  • Action:
    • ACTION_DIAL 打开拨号界面
    • ACTION_CALL 拨打电话(需要CALL_PHONE权限)
  • Data URI:
    • tel:<phone-number>
    • voicemail:<phone-number>
fun dialPhoneNumber(phoneNumber: String) {
    val intent = Intent(Intent.ACTION_DIAL).apply {
        data = Uri.parse("tel:$phoneNumber")
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

网页搜索

  • Action:ACTION_WEB_SEARCH
  • Extra:SearchManager.QUERY 搜索字符串
fun searchWeb(query: String) {
    val intent = Intent(Intent.ACTION_WEB_SEARCH).apply {
        putExtra(SearchManager.QUERY, query)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

设置

  • Action:
    • ACTION_SETTINGS
    • ACTION_WIRELESS_SETTINGS
    • ACTION_AIRPLANE_MODE_SETTINGS
    • ACTION_WIFI_SETTINGS
    • ACTION_APN_SETTINGS
    • ACTION_BLUETOOTH_SETTINGS
    • ACTION_DATE_SETTINGS
    • ACTION_LOCALE_SETTINGS
    • ACTION_INPUT_METHOD_SETTINGS
    • ACTION_DISPLAY_SETTINGS
    • ACTION_SECURITY_SETTINGS
    • ACTION_LOCATION_SOURCE_SETTINGS
    • ACTION_INTERNAL_STORAGE_SETTINGS
    • ACTION_MEMORY_CARD_SETTINGS
fun openWifiSettings() {
    val intent = Intent(Settings.ACTION_WIFI_SETTINGS)
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

发短信

  • Action:
    • ACTION_SENDTO
    • ACTION_SEND
    • ACTION_SEND_MULTIPLE
  • Data URI:
    • sms:<phone_number>
    • smsto:<phone_number>
    • mms:<phone_number>
    • mmsto:<phone_number>
  • MIME Type:
    • "text/plain"
    • "image/*"
    • "video/*"
  • Extra
    • "subject"
    • "sms_body"
    • EXTRA_STREAM
fun composeMmsMessage(message: String, attachment: Uri) {
    val intent = Intent(Intent.ACTION_SEND).apply {
        data = Uri.parse("smsto:")  // This ensures only SMS apps respond
        putExtra("sms_body", message)
        putExtra(Intent.EXTRA_STREAM, attachment)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

网页浏览器

  • Action:ACTION_VIEW
  • Data URI:
    • http:<URL>
    • https:<URL>
  • MIME Type:
    • "text/plain"
    • "text/html"
    • "application/xhtml+xml"
    • "application/vnd.wap.xhtml+xml"
fun openWebPage(url: String) {
    val webpage: Uri = Uri.parse(url)
    val intent = Intent(Intent.ACTION_VIEW, webpage)
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

示例intent-filter

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <!-- Include the host attribute if you want your app to respond
             only to URLs with your app's domain. -->
        <data android:scheme="http" android:host="www.example.com" />
        <category android:name="android.intent.category.DEFAULT" />
        <!-- The BROWSABLE category is required to get links from web pages. -->
        <category android:name="android.intent.category.BROWSABLE" />
    </intent-filter>
</activity>

使用adb调试Intent

adb shell am start -a <ACTION> -t <MIME_TYPE> -d <DATA> \
  -e <EXTRA_NAME> <EXTRA_VALUE> -n <ACTIVITY>

如:

adb shell am start -a android.intent.action.DIAL \
  -d tel:555-5555 -n org.example.MyApp/.MyActivity
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容