Intent
概述
翻译过来为“意图”,它是一种运行时绑定(run-time binding)机制,可以应用于两个应用间的通讯交互,也能够应用于在同一个应用下不同组件的交互(activity、service、broadcast receiver)
看下面的图,虽然Intent不属于四大组件,但是Intent却承担了三大组件的“中间人”的重任。试想一下,如果没有这个中间人,每一对组件间想要通信,就都需要重新开辟一条通道,随着组件数量和通讯需求的增加,这无疑会使得组件间的关系错综复杂,最后高度耦合,而加入这样一个中间人的角色,所有组件只需要联系这个中间人,由这个中间人去为组件进行匹配、传递等相关通信操作,这就使得组件间的依赖关系被极大的降低了。
(题外:Content Provide本身就是涉及进程通信的机制,所以用不到Intent)
对于向这三种组件发送intent有不同的机制:
- 使用Context.startActivity()或Activity.startActivityForResult(),传入一个intent来启动一个activity。
- 使用Activity.setResult(),传入一个intent来从activity中返回结果。
- 将intent对象传给Context.startService()来启动一个service或者传消息给一个运行的service。
- 将intent对象传给Context.bindService()来绑定一个service。
- 将intent对象传给Context.sendBroadcast(),Context.sendOrderedBroadcast(),或者Contex.sendStickyBroadcast()方法,则它们被传给 broadcast receiver。
然后上一段API对Intent的介绍
Intent 是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
- 启动 Activity:
Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。 如果您希望在 Activity 完成后收到结果,请调用 startActivityForResult()。在 Activity 的onActivityResult() 回调中,您的 Activity 将结果作为单独的 Intent 对象接收。
- 启动服务:
Service 是一个不使用用户界面而在后台执行操作的组件。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 描述了要启动的服务,并携带了任何必要的数据。 如果服务旨在使用客户端-服务器接口,则通过将 Intent 传递给 bindService(),您可以从其他组件绑定到此服务。
- 传递广播:
广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),您可以将广播传递给其他应用。
组成部分
Intent由以下各个组成部分:
- component(组件) :目的组件
- Action (动作) :用来表现意图的行动
- category (类别) :用来表现动作的类别
- data (数据) :表示与动作要操纵的数据
- type (数据类型) :对于data范例的描写
- extras (扩展信息) :扩展信息
- Flags (标志位) :期望这个意图的运行模式
更多细节可以参考API文档,传送门地址:
https://developer.android.google.cn/guide/components/intents-filters.html#Building
下面简单的对每个部分进行一定的介绍,具体肯定是????的更具体。
component
即明确指定Intent的目标组件,这部描述再下面的显式Intent,不重复描述。
Action&Category
Action用来表现意图的行动当日常生活中,描述一个意愿或愿望的时候,总是有一个动词在其中。
Category则为动作声明一个类别(给action增加额外的信息),常与action(难度不是必须吗?大雾)配套使用。
句子结构常常有主谓宾,Action即“谓语”,Category相当于“状语”
- 当你指明了一个Action,执行者就会依照这个动作的指示,接受相关输入,表现对应行为,产生符合的输出。
- 在Intent类中,定义了一批量的动作,比如 ACTION_VIEW, ACTION_PICK等, 基本涵盖了常用动作。
- Action是一个用户定义的字符串,用于描述一个Android应用程序组件,一个Intent Filter可以包含多个Action。
- 在 AndroidManifest.ml的Activity定义时可以在其节点指定一个Action列表用于标识Activity所能接受的“动作”。
- 在Intent对象中add的Category属性,在Intent Filter中必须出现,否则会直接报错!!
相关Demo统一放下方的Demo一块,可以点击右侧目录进行跳转。
同时系统提供很多默认的Action常量,可以参考API文档
https://developer.android.google.cn/reference/android/content/Intent.html#standard-activity-actions
同样的,也提供了许多category的常量,可以参考如下:
https://developer.android.google.cn/reference/android/content/Intent.html#standard-categories
Data&Type
Data即数据,表示要操纵的数据,声明方式类似Action&Category
Type即对data范例的描写
句子结构常常有主谓宾,Action即“宾语”,而Type可以理解为Application和activity的关系,有data那data的优先级就更高,没有就按type来。
- data常配合action一起使用,来描述一个意图(想想宾语也是搭配谓语的),Data用一个uri对象(数据的地址,一种标识符)来表示。
- 如果Intent对象中既包含Uri又包含Type,那么,在sintent-filter>中也必须二者都包含才能通过测试。
- Data属性的声明中要指定访问数据的Uri和MIME类型,Type属性则用于明确指定Data属性的数据类型或MIME类型。
- 通常来说,当Intent不指定Data属性时Type属性才会起作用,否则Android系统将会根据Data属性值来分析数据的类型,所以无需指定Type属性。
- 如果Intent对象包含Uri但是没有包含类型,并且类型不能从Uri中自动识别,那么cintent-filter>列表中也只能包含Uri
- 如果Intent对象只包含类型,没有包含Uri。那么,在sintent-filter>中也只能包含类型,不能包含Uri。
相关Demo统一放下方的Demo一块,可以点击目录进行跳转。
Extras
下面是API上的描述:
携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的 extra。
您可以使用各种 putExtra() 方法添加 extra 数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将Bundle 插入 Intent 中。
例如,使用 ACTION_SEND 创建用于发送电子邮件的 Intent 时,可以使用 EXTRA_EMAIL 键指定“目标”收件人,并使用 EXTRA_SUBJECT 键指定“主题”。
Intent 类将为标准化的数据类型指定多个 EXTRA_* 常量。如需声明自己的 extra 键(对于应用接收的 Intent),请确保将应用的软件包名称作为前缀。 例如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Extras是其他所有附加信息的集合,可以为组件提供扩展信息。(传值的时候,就是使用它来传递简单对象、数据等等)
关于Demo可以参考之前的文章:
https://blog.csdn.net/nishigesb123/article/details/88900360
下面不再提供单独的演示
Flags
同样上一段API的描述
在Intent 类中定义的、充当 Intent 元数据的标志。 标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。
更多信息可以参考:
https://developer.android.google.cn/reference/android/content/Intent.html#setFlags(int)
实际上指的是Intent的运行模式,一个程序启动后系统会为这个程序分配一个task。
一个task里面可以拥有不同应用程序的activity,一个程序也可以对应多个task。
可以在AndroidManifest.xml中activity标签的属性launchMode中设置它们的对应关系(Activity的加载模式),一共有以下四种模式:
- standard:标准模式,以这种模式加载必定会构造一个新的Activity实例放到目标task中的activity栈顶,不管当前 task的栈顶是什么情况。
- singleTop: 与standard模式类似,区别在于加载activity会多个判断步骤。判断需要加载的新activity与当前 task栈顶的activity是不是同一个,相同的话就不再构造新的activity,并调用这个activity的newlnstance ()方法,不相同就还是会构造新的activity放到栈顶。
- singleTask:该种模式下,会创建一个新的task来加iactivity,并且这个task中只允许存在一个Activity的一个实例(以后可以加载其他activity的实例)
- SingleInstance:这种模式下,会创建一个新的task并且这个task中只能存在一个需要加载的这个Activity实例,即除了这个activity之外,不允许其他activity。
关于 Activity 应该如何与任务关联 可以参考API地址如下:
https://developer.android.google.cn/guide/components/tasks-and-back-stack.html#ManifestForTasks
这部分内容比较多,所以单独放在另一篇文章(可能4.1号左右才可见):
https://blog.csdn.net/nishigesb123/article/details/88919008
下面不再提供单独的Demo演示
Intent大致可以分为两种, 显式和隐式,接下来我们逐个介绍。
显式Intent
显式 Intent 是指用于启动某个特定应用组件(例如,应用中的某个特定 Activity 或服务)的 Intent。
要创建显式 Intent,请为 Intent 对象定义组件名称 — Intent 的所有其他属性均为可选属性。
下面是一个简单的代码片段:
Intent intent = new Intent(MainActivity.this,YourActivity.class);
startActivity(intent);
可以看到,构建了一个Intent,传入了 MainActivity.this(上下文),YourActivity.class(目标活动,即component),即在MainActivity.this之上启动目标活动,如此一来,Intent非常明确,即称之为显示Intent。
目标活动可以是Class对,也可以是包名加类名的字符串。
隐式Intent
显示intent需要指定“接收者”和“发送者”,这不利于解耦,显然是和我们intent的初衷有一定矛盾之处的。
隐式就不一样了,Intent的发送者在构造Intent对象时,并不需要指定“接收者”,而是通过一定的设置,由系统进行筛选,这有助于解耦,所以官方也是推荐这种方式。
说到筛选,不难想到Filter(过滤器),确实,隐式Intent需要借助Intent Filter来实现“筛选”这一过程,并且仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。
下面是Intent Filter的匹配过程:
DEMO
首先测试显式Intent,备注:除了该方式,接下来其余测试都是隐式。
测试之前准备一个Main2Activity,用于跳转,内容可以什么都不写。
再准备一个Button,并添加点击事件
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="componentClick"
android:text="通过组件名称直接查找组件"
app:layout_constraintTop_toTopOf="parent"
/>
书写点击事件方法
//显式(指定组件名称)
public void componentClick(View view){
Intent intent = new Intent();
ComponentName componentName = new ComponentName(this,Main2Activity.class);
intent.setComponent(componentName);
//等价于 Intent intent = new Intent(this,Main2Activity.class);
startActivity(intent);
}
点击后成功跳转到Main2Activity
接着测试Action&Category
测试之前准备一个Main3Activity,用于跳转,内容可以什么都不写,为了标识,加了个TextView。
同样准备一个Button,并添加点击事件
<Button
android:id="@+id/button_action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="actionClick"
android:text="通过Action查找组件"
app:layout_constraintTop_toBottomOf="@id/button"
/>
书写点击事件方法
//隐式(通过action)
public void actionClick(View view){
Intent intent = new Intent();
//参数为字符串,理论上可以随便写,规范点加上包名(大雾)
intent.setAction("com.example.a3_30intent.action.MY_ACTION");
//同样可以合并成一行 Intent intent = new Intent("com.example.a3_30intent.action.MY_ACTION");
//指定category
//intent.addCategory("com.example.a3_30intent.action.MY_Category");
startActivity(intent);
}
进入配置清单文件(AndroidManifest.xml)
在Main3Activity部分加入intent-filter
定义子项action,为其name赋值com.example.a3_30intent.action.MY_ACTION(setAction的那个参数)
需要注意的:
- 一个Activity是可以指定多个action属性的
- 一个Activity也可以指定多个category属性
- 一个Intent只能指定一个action
- 一个intent可以指定多个category
最后还要加上一句配置category的语句(可以暂时理解成默认格式....)
注意:如果当前组件是Activity,并且没有指定的category,必须加上category并使用默认的DEFAULT!!!如下:
<category android:name="android.intent.category.DEFAULT"></category>
<activity android:name=".Main3Activity">
<intent-filter>
<action android:name="com.example.a3_30intent.action.MY_ACTION"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
点击后成功跳转到Main3Activity
可以试着在之前的Main2Activity的配置文件也加入
<action android:name="com.example.a3_30intent.action.MY_ACTION"></action>
<category android:name="android.intent.category.DEFAULT"></category>
即,有两个符合的Activity,
可以看到系统会弹出选择框,示意你从两个中选一个...(感觉还是给Main3Activity设置一下label比较好...不然名字显示是application的label...)
可以通过android:priority来设置优先级(本身是数字越大优先级越高,但是在此处,除非有一个数字为负数,比如一个为1,一个为2,优先级将视相同,即依旧会弹出提示框让用户选择)
<activity
android:name=".Main3Activity">
<intent-filter android:priority="-1">
<action android:name="com.example.a3_30intent.action.MY_ACTION"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter android:priority="2">
<action android:name="com.example.a3_30intent.action.MY_ACTION"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
接下来关于data&type
再准备一个Main4Activity,用于跳转,内容可以什么都不写。
依旧需要一个Button,并添加点击事件
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="componentClick"
android:text="通过组件名称直接查找组件"
app:layout_constraintTop_toTopOf="parent"
/>
书写点击事件方法
由于setdata参数是Uri,所以需要定义一个Uri。
//隐式(通过data)一般配合action
public void dataClick(View view){
Intent intent = new Intent();
intent.setAction("com.example.a3_30intent.action.MY_ACTION");
//Uri data = Uri.fromFile();
Uri data = Uri.parse("http://www.baidu.com");
//参数为一个uri
intent.setData(data);
startActivity(intent);
}
然后是配置文件
需要配置如下属性
android:scheme(协议,如http)
android:host(具体的地址,如本例中的www.baidu.com)
android:path(具体的路径,如果是www.baidu.com/abc/abc...则可以再补path,实际上好像path可以代替host?)
<activity
android:name=".Main4Activity"
android:label="@string/title_activity_main4"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter android:priority="1">
<action android:name="com.example.a3_30intent.action.MY_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="www.baidu.com"></data>
</intent-filter>
</activity>
点击后成功跳转到Main4Activity
如果不使用自定义的action,而是使用默认的如intent.ACTION_VIEW
并在配置文件清单加入
<action android:name="android.intent.action.VIEW" />
可能还需要设置以下语句,否则飘红线...
<category android:name="android.intent.category.BROWSABLE"/>
可以看到会打开浏览器(也可能是出来一个选择,选用浏览器还是直接在Main4Activity打开)
访问百度...(← ←甭管地址栏,虽然访问的是www.baidu.com但是它自己重定向了)
下面再设置type
首先我们观察一下setData方法和setType方法在系统中的定义
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
可以发现,setData方法中默认Type被设置为null,setType方法中Data被设置为null
显然他俩是相互矛盾的,所以想两个方法该怎么办呢?
系统提供了一个
intent.setDataAndType()方法
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
我们使用它就可以同时对type和data进行设置了
测试代码如下:
intent.setDataAndType(data,"text/html");
配置文件:
<activity
android:name=".Main4Activity"
android:label="@string/title_activity_main4"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter android:priority="1">
<action android:name="com.example.a3_30intent.action.MY_ACTION" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http" android:host="www.baidu.com" android:mimeType="text/html"></data>
</intent-filter>
</activity>
测试效果和原来没有差别,所以实际上type并不常用....效果不算明显