Intent学习
构建 Intent
Intent
对象携带 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。
Intent
中包含的主要信息如下:
- 组件名称
- 要启动的组件名称。
这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。如果没有组件名称,则 Intent 则为隐式,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。如需在应用中启动特定的组件,则应指定该组件的名称。
请注意:启动
Service
时,应始终指定组件名称。否则,您无法确定哪项服务会响应 Intent,且用户无法看到哪项服务已启动。Intent
的这一字段是ComponentName
对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。例如,com.example.ExampleActivity
。您可以使用setComponent()
、setClass()
、setClassName()
,或Intent
构造函数设置组件名称。
- 操作
- 指定要执行的通用操作(例如,查看或选取)的字符串。
对于广播 Intent,这是指已发生且正在报告的操作。操作会在很大程度上决定其余 Intent 的构成,特别是数据和 extra 中包含的内容。
您可以指定自己的操作,供 Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由
Intent
类或其他框架类定义的操作常量。以下是一些用于启动 Activity 的常见操作:ACTION_VIEW
- 如果您拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查看的地址),请通过 Intent 将此操作与
startActivity()
结合使用。 ACTION_SEND
- 这也称为共享 Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 将此操作与
startActivity()
结合使用。
有关更多定义通用操作的常量,请参阅
Intent
类参考文档。其他操作在 Android 框架中的其他位置定义。例如,对于在系统的设置应用中打开特定屏幕的操作,将在Settings
中定义。您可以使用
setAction()
或Intent
构造函数为 Intent 指定操作。如以下示例所示,如果定义自己的操作,请确保加入应用的软件包名称作为前缀:
- 数据
- 引用待操作数据和/或该数据 MIME 类型的 URI(
Uri
对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是ACTION_EDIT
,则数据应包含待编辑文档的 URI。创建 Intent 时,除了指定 URI 以外,指定数据类型(其 MIME 类型)往往也很重要。例如,能够显示图像的 Activity 可能无法播放音频文件,即便 URI 格式十分类似时也是如此。因此,指定数据的 MIME 类型有助于 Android 系统找到接收 Intent 的最佳组件。但,有时 MIME 类型可以从 URI 中推断得出,特别当数据是
content:
URI 时尤其如此。content:
URI 表明数据位于设备中,且由ContentProvider
控制,这使得数据 MIME 类型对系统可见。要仅设置数据 URI,请调用
setData()
。要仅设置 MIME 类型,请调用setType()
。如有必要,您可以使用setDataAndType()
同时显式设置二者。注意:若要同时设置 URI 和 MIME 类型,请勿调用
setData()
和setType()
,因为它们会互相抵消彼此的值。请始终使用setDataAndType()
同时设置 URI 和 MIME 类型。
- 类别
- 一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。以下是一些常见类别:
CATEGORY_BROWSABLE
- 目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。
CATEGORY_LAUNCHER
- 该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。
有关类别的完整列表,请参阅
Intent
类描述。您可以使用
addCategory()
指定类别。
以上列出的这些属性(组件名称、操作、数据和类别)表示 Intent 的既定特征。通过读取这些属性,Android 系统能够解析应当启动哪个应用组件。但是,Intent 也有可能会携带一些不影响其如何解析为应用组件的信息。Intent 还可以提供以下信息:
- Extra
- 携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。
您可以使用各种
putExtra()
方法添加 extra 数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有 extra 数据的Bundle
对象,然后使用putExtras()
将Bundle
插入Intent
中。例如,使用
ACTION_SEND
创建用于发送电子邮件的 Intent 时,可以使用EXTRA_EMAIL
键指定目标收件人,并使用EXTRA_SUBJECT
键指定主题。Intent
类将为标准化的数据类型指定多个EXTRA_*
常量。如需声明自己的 extra 键(对于应用接收的 Intent),请确保将应用的软件包名称作为前缀,如下例所示:注意:在发送您希望另一个应用接收的 Intent 时,请勿使用
Parcelable
或Serializable
数据。如果某个应用尝试访问Bundle
对象中的数据,但没有对打包或序列化类的访问权限,则系统将提出一个RuntimeException
。 - 标志
- 标志在
Intent
类中定义,充当 Intent 的元数据。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,Activity 是否属于最近的 Activity 列表)。如需了解详细信息,请参阅
setFlags()
方法。
显式 Intent 示例
1. 构造函数
Intent intent = new Intent(FirstActivity.this,SecondActivity.class); startActivity(intent);
2. setClassName
Intent intent = new Intent(); //表示希望启动com.example.test包中的com.example.test.MainActivity intent.setClassName("com.example.test","com.example.test.MainActivity"); startActivity(intent);
3. setClass
Intent intent = new Intent(); //启动packageContext包里的OtherActivity.class类; intent.setClass(Context packageContext, OtherActivity.class); startActivity(intent);
隐式 Intent示例
// Create the text message with a string Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); sendIntent.setType("text/plain"); // Verify that the intent will resolve to an activity if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(sendIntent); }
调用 startActivity() 时,系统将检查已安装的所有应用,确定哪些应用能够处理这种 Intent(即:含 ACTION_SEND 操作并携带“text/plain”数据的 Intent)。如果只有一个应用能够处理,则该应用将立即打开并为其提供 Intent。如果多个 Activity 接受 Intent,则系统将显示一个对话框(如图 2 所示),使用户能够选取要使用的应用。
这是Intent的另一个构造函数,直接将action的字符串传进来,表明我们想要启动能够相应com.example.activitytest.ACTION_START这个action的活动。
button1.setOnClickListener { Toast.makeText(this,"You clicked Button1",Toast.LENGTH_SHORT).show() val intent = Intent("com.example.activitytest.ACTION_START") startActivity(intent) }
能启动SecondActivity的原因,是因为SecondActivity的category标签使用的是默认值DEFAULT。在调用 startActivity()时,会自动传入 默认的category。
接收隐式 Intent
要公布应用可以接收哪些隐式 Intent,请在清单文件中使用 <intent-filter>
元素为每个应用组件声明一个或多个 Intent 过滤器。每个 Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 Intent 类型。仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。
请注意:显式 Intent 始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此。
应用组件应当为自身可执行的每个独特作业声明单独的过滤器。例如,图像库应用中的一个 Activity 可能会有两个过滤器,分别用于查看图像和编辑图像。当 Activity 启动时,将检查 Intent
并根据 Intent
中的信息决定具体的行为(例如,是否显示编辑器控件)。
每个 Intent 过滤器均由应用清单文件中的 <intent-filter>
元素定义,并嵌套在相应的应用组件(例如,<activity>
元素)中。在 <intent-filter>
内部,您可以使用以下三个元素中的一个或多个指定要接受的 Intent 类型:
<action>
- 在
name
属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。 <data>
- 使用一个或多个指定数据 URI(
scheme
、host
、port
、path
)各个方面和 MIME 类型的属性,声明接受的数据类型。 <category>
- 在
name
属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。请注意:要接收隐式 Intent,必须将
CATEGORY_DEFAULT
类别包括在 Intent 过滤器中。方法startActivity()
和startActivityForResult()
将按照其声明CATEGORY_DEFAULT
类别的方式处理所有 Intent。如果未在 Intent 过滤器中声明此类别,则隐式 Intent 不会解析为您的 Activity。
例如,以下是一个使用包含 Intent 过滤器的 Activity 声明,当数据类型为文本时,系统将接收 ACTION_SEND
Intent :
<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> </activity>
向下一个Activity传递数据
Intent不仅可以用来打开其它的Activity,还可以用来进行数据传输。
传输方法很简单,Intent提供了一系列的 putExtra() 方法重载,可以把想要传输的数据暂时封存到Intent中,启动新的Activity后,再从Intent中取出。
打开Activity 时,先将strData存放在了intent中,代码如下:
button1.setOnClickListener { val strData = "Message" val intent = Intent(this, SecondActivity::class.java) intent.putExtra("extra_name", strData) startActivity(intent) }
取出代码如下,需要在SecondActivity 的 onCreate() 函数中自行取出,代码如下:
class SecondActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.second_layout) val extraData = intent.getStringExtra("extra_data") Log.d("SecondActivity","extra data is $extraData") } }
代码中的intent实际上是调用父类的getIntent()方法,从而来获得启动SecondActivity 的intent。然后调用getStringExtra()方法并传入相应的键值,就可以得到传递的数据了。这里由于我们传递的是字符串,所以使用getStringExtra()方法来获取传递的数据。如果传递的是整型数据,则使用getIntExtra()方法;如果传递的是布尔型数据,则使用getBooleanExtra()方法,以此类推。
向上一个Activity传递数据
由于返回上一个Activity,仅需back即可。没有专门的Intent,并不能像向下传递消息一样。
Activity类提供了一个启动Activity的 startActivityForResult() 方法。
button1.setOnClickListener { val intent = Intent(this, SecondActivity::class.java) startActivityForResult(intent, 1) }
接下来在SecondActivity中添加点击事件:
button2.setOnClickListener { val intent = Intent() intent.putExtra("data_return", "Hello FirstActivity") setResult(RESULT_OK, intent) finish() }
着同样创建了一个intent,并没有意图,仅用来进行数据传输。
紧接着把要传递的数据存放在Intent中,然后调用了setResult()方法。这个方法非常重要,专门用于向上一个Activity返回数据。
setResult()方法接收两个参数:
第一个参数用于向上一个Activity返回处理结果,一般只使用RESULT_OK或RESULT_CANCELED这两个值;
第二个参数则把带有数据的Intent传递回去。最后调用了finish()方法来销毁当前Activity。
Tips:当前返回数据是通过SecondActivity中的按钮来返回,但是一般返回上一Activity直接使用back,因此需要另一种写法:
由于我们是使用startActivityForResult()方法来启动SecondActivity的,在SecondActivity被销毁之后会回调上一个Activity的onActivityResult()方法,因此我们需要在FirstActivity中重写这个方法来得到返回的数据,如下所示:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { 1 -> if (resultCode == RESULT_OK) { val returnedData = data?.getStringExtra("data_return") Log.d("FirstActivity", "returned data is $returnedData") } } }
onActivityResult()方法带有3个参数:第一个参数requestCode,即我们在启动Activity时传入的请求码;第二个参数resultCode,即我们在返回数据时传入的处理结果;第三个参数data,即携带着返回数据的Intent。由于在一个Activity中有可能调用startActivityForResult()方法去启动很多不同的Activity,每一个Activity返回的数据都会回调到onActivityResult()这个方法中,因此我们首先要做的就是通过检查requestCode的值来判断数据来源。确定数据是从SecondActivity返回的之后,我们再通过resultCode的值来判断处理结果是否成功。最后从data中取值并打印出来,这样就完成了向
上一个Activity返回数据的工作。
但是目前版本已经被Google淘汰,需要使用更新的 Activity Result API,用法如下:
参考
Activity Result API详解,是时候放弃startActivityForResult了_guolin的博客-CSDN博客_startactivityforresult废弃
如果你将项目中的appcompat库升级到1.3.0或更高的版本,你会发现startActivityForResult()方法已经被废弃了。这个方法相信所有做过Android的开发者都用过,它主要是用于在两个Activity之间交换数据的。那么为什么这个如此常用的方法会被废弃呢?官方给出的说法是,现在更加建议使用Activity Result API来实现在两个Activity之间交换数据的功能。我个人的观点是,startActivityForResult()方法并没有什么致命的问题,只是
private val requestDataLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ if (it.resultCode == RESULT_OK) { val returnData = it.data?.getStringExtra("data_return") Log.d("FirstActivity","data return is $returnData") } }
通过 registerForActivityResult() 来注册对Activity的结果的监听。
registerForActivityResult()方法接收两个参数:
第一个参数是一种Contract类型,由于我们是希望从另外一个Activity中请求数据,因此这里使用了StartActivityForResult。
第二个参数是一个Lambda表达式,当有结果返回时则会回调到这里,然后我们在这里获取并处理数据即可。
参考:
https://developer.android.google.cn/guide/components/intents-filters
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理