Intent的理解和使用
Intent的理解和使用
1、什么是Intent
Intent的中文意思是“意图,意向”,在Android中提供了Intent机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动 作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的 交互。因此,可以将Intent理解为不同组件之间通信的“媒介”专门提供组件互相调用的相关信息。
总之,Intent具有激活组件和携带数据的功能!
启动三大组件
下面是Intent启动不同组件的部分方法:
Activity组件
startActivity(Intent intent);
startActivityForResult(Intent intent,int requestCode);
Service组件:
startService(Intent intent);
bindService(Intent intent,ServiceConnection conn,int flags);
BroadcastReceiver组件:
sendBroadcast(Intent intent);
sendOrderedBroadcast(Intent intent,String receiverPermission);
2、Intent的构成
Intent 作为一个负责组件间传递消息的信息对象,最重要的就是其包含的信息。实际上无论是显式还是隐式,Intent 发出的时候,系统对应的行为正是由 Intent 所包含信息的组合决定。一个 Intent 所包含的信息如下图:
Intent对象大致包括7大属性:Action(动作)、Data(数据)、Category(类别)、Type(数据类型)、Component(组件)、Extra(扩展信息)、Flag(标志位)。其中最常用的是Action属性和Data属性。
根据信息的作用用于,又可分为三类:
- 1、Component Name、Action、Data、Category
这4中信息决定了Android会启动哪个组件,其中Component Name用于在显式Intent中使用,Action、Data、Category、Extras、Flags用于在隐式Intent中使用。
- 2、Extras:里面包含了具体的用于组件实际处理的数据信息。
- 3、Flags:其是Intent的元数据,决定了Android对其操作的一些行为。
2.1、Action:用来表现意图的行动
一个字符串变量,可以用来指定Intent要执行的动作类别。比如查看或选择,其对应着Intent Filter中的action标签。
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
常见的action有:
Activity Actions
Broadcast Actions:
2.2、Data:表示与动作要操纵的数据
Intent中的data指的是Uri对象和数据的MIME类型,其对应着Intent Filter中的data标签。
一个完整的Uri由scheme、host、port、path组成,格式是<scheme>://<host>:<port>/<path>
例如:
content://com.example.project:200/folder/subfolder/etc。
Uri就像一个数据链接,组件可以根据此Uri获得最终的数据来源。通常将Uri和action结合使用,比如我们将action设置为ACTION_VIEW,我们应该提供将要被编辑修改的文档的Uri。
2.3、Category:用来表现动作的类别
一个包含Intent额外信息的字符串,表示哪种类型的组件来处理这个Intent。任何数量的Category 描述都可以添加到Intent中,但是很多intent不需要category,下面列举一些常用的category:
<!-- 必须指定CATEGORY_DEFAULT,只有这样startActivity(intent)才能找到 -->
<category android:name="com.baidu.category.TEST" />
<category android:name="android.intent.category.DEFAULT" />
2.4、Type:指定数据类型
一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
intent.setDataAndType(Uri.parse("baidu://www.baidu.com/news"), "image/jpeg");
<data android:mimeType="image/*" android:scheme="baidu" android:host="www.baidu.com" android:path="/news"/>
2.5、Component:目的组件
指定Intent的目标组件名称,当指定了这个属性后,系统将跳过匹配其他属性,而直接匹配这个属性来启动对应的组件
通过这个方法建立一个Intent对象,然后将该对象传递给Activity的startActivity(Intent intent)方法即可启动目标组件,实例代码如下:
Intent intent = new Intent(this,Activity02.class); //创建Intent对象
startActivity(intent); //开启Activity02
2.6、Extra:扩展信息
Intent可以携带的额外 key-value 数据,你可以通过调用putExtra()
方法设置数据,每一个 key对应一个 value数据。你也可以通过创建 Bundle对象来存储所有数据,然后通过调用putExtras()
方法来设置数据。
2.7、Flag:期望这个意图的运行模式
用来指示系统如何启动一个Activity,可以通过setFlags()
或者addFlags()
可以把标签flag用在Intent中。
3、Intent的用法
3.1、显式Intent
显式 Intent 通常应用在自己的程序中,启动特定组件。用法比较简单,就是构造一个带有目标组件名的 Intent,作为参数传入上述方法即可,调用方法后会直接启动相应组件。
1)构造方法传入Component,最常用的方式:
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
2)setComponent方法
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
//或者intent.setClassName(this, "com.example.app.SecondActivity");
//或者intent.setClassName(this.getPackageName(),"com.example.app.SecondActivity");
startActivity(intent);
3)setClass / setClassName方法
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
//或者intent.setClassName(this, "com.example.app.SecondActivity");
//或者intent.setClassName(this.getPackageName(),"com.example.app.SecondActivity");
startActivity(intent);
显式Intent通过Component可以直接设置需要调用的Activity类,可以唯一确定一个Activity,意图特别明确,所以是显式的。设置这个类的方式可以是Class对象(如SecondActivity.class),也可以是包名加类名的字符串(如"com.example.app.SecondActivity")。
3.2、隐式Intent
隐式 Intent 允许启动其他应用中的组件,在调用发送 Intent 的方法后,该 Intent 会交由 Android 系统进行匹配,(匹配根据信息是 action、data、category 这3项,即本文第一张图片 Intent 包含信息中标蓝色部分)筛选出整个设备可响应该 Intent 的组件。下图官方文档对隐式 Intent 如何传递启动其他应用组件的图解:筛选是根据所有的<intent-filter>
来筛选。
隐式 Intent 如何通过系统传递以启动其他 Activity 的图解: [1] Activity A 创建包含操作描述的 Intent,并将其传递给 startActivity()。 [2] Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。 找到匹配项之后, [3] 该系统通过调用匹配 Activity(Activity B)的 onCreate() 方法并将其传递给 Intent,以此启动匹配 Activity。
3.2.1、Intent过滤器
Intent 过滤器是 manifests 里组件的子标签,一个控件可以声明一个或者多个 Intent 过滤器,只要其中一个通过匹配,该组件就可以相应相应 Intent。先来看一个官方给出的 Intent 过滤器示例:
<activity android:name="MainActivity">
<!-- 应用的首页面,会显示在启动器中 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity">
<!-- 该活动可以处理 SEND这个 aciton,且处理数据类型为无格式文本 -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<!--需要响应隐式Intent的活动必须添加 android.intent.category.DEFAULT这个分类,因为starActivity()方法会默认为Intent添加-->
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- 此为同一个 Activity 的第二个过滤器 该活动可以处理 "SEND" 和 "SEND_MULTIPLE"两种 aciton 处理数据类型为多媒体数据(包括图片、视频和全景照片) -->
<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>
这里值得注意的几个点:
- 1、 Intent 只能有1个 action,而 Intent 过滤器可有多个 action
- 2、需要响应隐式 Intent 的 Activity 的 Intent 过滤器中必须添加
android.intent.category.DEFAULT
这个分类 - 3、 data 在构造时传入的是 Uri 对象以及 mimeType 字符串,mimeType 的对应关系比较直观不详述,Uri对象会被分为 4个部分
<scheme>://<host>:<port>/<path>(<协议>://<主机名>:<端口>/<路径>)
,在 Intent过滤器中的写法如下:
<intent-filter>
<data
android:scheme="content" android:host="com.example.project"
android:port="200" android:path="/folder/subfolder/etc"/>
</intent-filter>
对应的Uri对象:
Uri uri=Uri.parse("content://com.example.project:200/folder/subfolder/etc");
- 4、 path 和 mimeType 允许使用*通配符,实现部分匹配。
- 5、对于所有 Activity,必须在清单文件中声明 Intent 过滤器。
但是,广播接收器的过滤器可以通过调用 registerReceiver() 动态注册。 且可以使用 unregisterReceiver() 注销该接收器。这样一来,应用便可仅在应用运行时的某一指定时间段内侦听特定的广播。
匹配规则
- 隐式 Intent 会与所有设备所有应用组件的 Intent 过滤器进行比对。
- 一个组件可以有多个 Intent 过滤器,某个 Intent 只需与其中任何一个匹配即可启动组件。
- 系统会根据 Intent 与 Intent 过滤器的 action、data、category 进行三次比对(测试),要全部通过才能匹配。
- action 的匹配规则:
A、Intent 只能包含一项 action,而 Intent 过滤器可以包含多项 action,只要 Intent 的 action 可以在 Intent 过滤器中找到对应项即可通过测试。
B、当 Intent 过滤器未声明任何 action 时,任何 Intent 都不能通过匹配。
C、当 Intent 未指定 Action 时,无法通过匹配。 - category 的匹配规则:
A、Intent 可以包含多项 category,Intent 过滤器也可以包含多项 category。Intent 中的每项 category 必须在Intent过滤器中都有对应项,才能通过匹配。
B、当目标组件为 Activity 时,如需相应隐式 Intent,必须添加 “android.intent.category.DEFAULT” 到 Intent 过滤器中。因为启动Activity的方法都会默认为Intent添加该 category。
C、目标组件为广播时,Intent 和 Intent 过滤器都不设置 category,可通过匹配。 - data的匹配规则:
和上面两个元素不同,data 具有子元素,其构成如下图:
在 Intent 中传入的 Uri 对象会被解析成<scheme>://<host>:<port>/<path>(<协议>://<主机名>:<端口>/<路径>)
4 个部分进行匹配测试。4 项均为可选,但是存在线性依赖关系:
- 如果未指定 scheme,则会忽略 host;
- 如果未指定 host,则会忽略 port;
- 如果未指定 scheme 和 host,则会忽略 path。
data 的匹配规则主要有以下几点:
A、将 Intent 中的 URI 与过滤器中的 URI 规范进行比较时,它仅与过滤器中包含的部分 URI 进行比较。 例如:
- 如果过滤器仅指定 scheme,则具有该 scheme 的所有 URI 均与该过滤器匹配。
- 如果过滤器指定 scheme 和 host,但未指定 path,则具有相同 scheme 和 host 的所有 URI
都会通过过滤器,无论其 path 如何均是如此。 - 如果过滤器指定 scheme、host和path,则仅具有相同 scheme、host 和 path 的 URI 才会通过过滤器。
- path 部分可以使用*通配符,仅需部分匹配路径名即可。
B、mimeType 可以部分使用通配符,如:image/(表示匹配所有格式图像数据),也可以全部使用/* 表示匹配所有类型数据。
C、当Intent同时不指定 uri 与 mimeType 时,只有同样未声明 uri 与 mimeType 的 Intent 过滤器可以通过匹配。
D、当 Intent 只含有 uri 时,只有声明 uri 相互匹配,且未声明 mimeType 的 Intent 过滤器可以通过匹配。
E、当 Intent 只含有 mimeType 时,只有 mimeType 相互匹配,且未声明 uri 的 Intent 过滤器可以通过匹配。
F、当 Intent 同时含有 uri 和 mimeType 时,只有两部分均匹配的 Intent 过滤器可以通过匹配。
G、Intent过滤器只声明mimeType时,默认支持scheme为content: 和 file: 的uri。
H、当 Intent 传入的 uri 为 content: URI 时,表明数据位于设备中,且由 ContentProvider 控制,此时即使不设置 mimeType,mimeType 也对系统可见。
非空判断
当隐式 Intent 发出而找不到匹配 Activity 时,调用将会失败,且应用会崩溃。要验证是否存在会接收 Intent 的 Activity ,可以对 Intent 对象调用 resolveActivity()。如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用 startActivity()。 如果结果为空,则不应使用该 Intent,应停用发出该 Intent 的功能。
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// 判断是否存在能够匹配该 Intent 的 Activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
3.2.2、举例
下面以Action为例:
AndroidManifest.xml文件中,首先被调用的Activity要有一个带有<intent-filter>
并且包含的Activity,设定它能处理的Intent,并且category设为"android.intent.category.DEFAULT"。action的name是一个字符串,可以自定义,例如这里设成为"mark":
<activity
android:name="com.example.app.SecondActivity">
<intent-filter>
<action android:name="mark"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
然后,在MainActivity,才可以通过这个action name找到上面的Activity。下面两种方式分别通过setAction和构造方法方法设置Action,两种方式效果相同。
1)setAction方法
Intent intent = new Intent();
intent.setAction("mark");
startActivity(intent);
2)构造方法直接设置Action
Intent intent = new Intent("mark");
startActivity(intent);
为了防止应用程序之间互相影响,一般命名方式是包名+Action名,例如这里命名"mark"就很不合理了,就应该改成"com.example.app.Test"
。
3.3、数据传送
Intent 作为组件间的信息对象,另一个主要作用就是数据的传送。
3.3.1、使用方法
putExtra()、Bundle方式
3.3.2、可传递的数据类型
Intent传送数据是以键值对的形式,主要通过putExtra()方法,该方法接收两个参数
- 第一个是数据的键,第二个是数据的值。
- 第二个参数的取值范围:
- a. 8种基本数据类型(
boolean byte char short int long float double
)、String
- b.
Intent
、Bundle
- c.
Serializable
对象、Parcelable
及其对应数组、CharSequence
类型 - d.
ArrayList
,泛型参数类型为:<Integer>
、<? Extends Parcelable>
、<Charsequence>
、<String>
- a. 8种基本数据类型(
在目标组件中取出 Intent 的方法根据数据类型有非常多,这里不一一列举,只给出一般格式。
3.3.3、具体使用
在当前Activity把要传递的数据暂存在Intent中、在新启动的Activity中取出Intent中的数据
方法1:putExtra()
// 目的:将FristActivity中的一个字符串传递到SecondActivity中,并在SecondActivity中将Intent对象中的数据(FristActivity传递过来的数据)取出
// 1. 数据传递
// a. 创建Intent对象(显示Intent)
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
// b. 通过putExtra()方法传递一个字符串到SecondActivity;
// putExtra()方法接收两个参数:第一个是键,第二个是值(代表真正要传递的数据)
intent.putExtra("data","I come from FirstActivity");
// c. 启动Activity
startActivity(intent);
// 2. 数据取出(在被启动的Activity中)
// a. 获取用于启动SecondActivit的Intent
Intent intent = getIntent();
// b. 调用getStringExtra(),传入相应的键名,就可得到传来的数据
// 注意数据类型 与 传入时保持一致
String data = intent.getStringExtra("data");
方法2:Bundle
// 1. 数据传递
// a. 创建Intent对象(显示Intent)
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
// b. 创建bundle对象
Bundle bundle = new Bundle();
// c. 放入数据到Bundle
bundle.putString("name", "carson");
bundle.putInt("age", 28);
// d. 将Bundle放入到Intent中
intent.putExtras(bundle);
// e. 启动Activity
startActivity(intent);
// 2. 数据取出(在被启动的Activity中)
// a. 获取用于启动SecondActivit的Intent
Intent intent = getIntent();
// b. 通过Intent获取bundle
Bundle bundle = intent.getExtras();
// c. 通过bundle获取数据传入相应的键名,就可得到传来的数据
// 注意数据类型 与 传入时保持一致
String nameString = bundle.getString("name");
int age = bundle.getInt("age");
两种方式的区别
- Bundle 意为 捆绑 的意思,更多适用于:
- 连续传递数据
- 若需实现连续传递:
Activity A -> B -> C
;- 若使用
putExtra()
,则需写两次intent , A->B先写一遍,在B中取出来 ,再把值重新写到Intent,再跳到C; - 若使用 Bundle,则只需取出 & 传入 Bundle对象即可
- 若使用
- 可传递的值:
对象
putExtra
()无法传递对象,而Bundle
则可通过putSerializable
传递对象
但传递的对象要实现Serializable接口
// 如传递User类的对象
public class User implements Serializable {
...
}
// 传递时
User user = new User();
Intent intent = new Intent(MyActivity.this,OthereActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("user", user);
intent.putExtras(bundle);
- 而
putExtra()
更多使用于单次传递
、传递简单数据
类型的应用场景
更多使用
//Activity间的数据传递
// 1.直接向intent对象中传入键值对(相当于Intent对象具有Map键值对功能)
intent.putExtra("first", text1.getText().toString());
intent.putExtra("second", text2.getText().toString());
// 2.新建一个Bundle对象 ,想该对象中加入键值对,然后将该对象加入intent中
Bundle bundle = new Bundle();
bundle.putString("first", "zhang");
bundle.putInt("age", 20);
intent.putExtras(bundle);
// 3.向intent中添加ArrayList集合对象
intent.putIntegerArrayListExtra(name, value);
intent.putIntegerArrayListExtra(name, value);
// 4.intent传递Object对象(被传递的对象的类实现Parcelable接口,或者实现Serialiable接口)
public Intent putExtra(String name, Serializable value)
public Intent putExtra(String name, Parcelable value)
数据回传
把数据传回上个活动也是 Intent 比较常用的方法之一。
实现的方式是在启动活动时使用startActivityForResult(Intent intent,int requestCode)
代替startActivity(Intent intent)
,当被启动活动销毁时,就会携带一个 Intent 回调到调用startActivityForResult()
方法的Activity的startActivityForResult()
方法。关于 Intent 添加 Extra,和数据的取出和普通的用法并没有什么区别。
基本的步骤是:
- A、调用
startActivityForResult()
方法启动新 Activity,该方法有两个参数,参 1 为 Intent,参 2为 int 类型的唯一请求码,用于判断数据来源。 - B、在新 Activity 中调用
setResult()
方法把携带希望回传数据的 Intent 作为参数传入。 - C、重写原 Activity 的
onActivityResult()
方法。该方法携带 3 个参数,参 1 为启动 Activity的请求码 requestCode,参 2 为表示处理结果是否成功的 resultCode,参 3 为携带数据的 Intent。
重写的主要逻辑是:先通过 requestCode 判断数据来源(根据场景,原 Activity 可能启动不同新 Activity);然后通过 resultCode 判断处理结果是否成功;最后取出 Intent 中数据进行处理即可。
//原 Activity 中启动新 Activity 并请求返回数据
Intent intent = new Intent(this,TagerActivity.class);
startActivityForResult(intent,1);
------------------------------------
//新 Activity 中设定返回 Intent 并销毁,销毁后会回调到原 Activity 的 onActivityResult()方法
Intent intent=new Intent();
intent.putExtra("extra_boolean",true);
setResult(RESULT_OK,intent);
finish();
------------------------------------
//原 Activity 中重写 onActivityResult() 方法
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case 1:
if (resultCode==RESULT_OK){
boolean b=data.getBooleanExtra("extra_boolean",false);
}
break;
default:
}
}
4、Intent常见应用(转)
参考
1、https://blog.csdn.net/qq_37567866/article/details/80565042
2、https://www.jianshu.com/p/67d99a82509b
3、https://www.jianshu.com/p/19147a69e970
4、https://www.jianshu.com/p/19147a69e970
5、不同组件间 传递数据