【正文】
intent组件虽然不是四大组件,但却是连接四大组件的桥梁,学习好这个知识,也非常的重要。
一、什么是intent
1、intent的概念:
android中提供了intent机制来协助应用间的交互与通讯,或者采用更准确的说法是,intent不仅可用于应用程序之间,也可用于应用程序内部的activity, service和broadcast receiver之间的交互。intent这个英语单词的本意是“目的、意向、意图”。
intent是一种运行时绑定(runtime binding)机制,它能在程序运行的过程中连接两个不同的组件。通过intent,你的程序可以向android表达某种请求或者意愿,android会根据意愿的内容选择适当的组件来响应。
activity、service和broadcast receiver之间是通过intent进行通信的,而另外一个组件content provider本身就是一种通信机制,不需要通过intent。我们来看下面这个图就知道了:

如果activity1需要和activity2进行联系,二者不需要直接联系,而是通过intent作为桥梁。通俗来讲,intnet类似于中介、媒婆的角色。
2、对于向这三种组件发送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(),或者context.sendstickybroadcast()等广播方法,则它们被传给 broadcast receiver。
二、intent的相关属性:
intent由以下各个组成部分:
component(组件):目的组件
action(动作):用来表现意图的行动
category(类别):用来表现动作的类别
data(数据):表示与动作要操纵的数据
type(数据类型):对于data范例的描写
extras(扩展信息):扩展信息
flags(标志位):期望这个意图的运行模式
intent类型分为显式intent(直接类型)、隐式intent(间接类型)。官方建议使用隐式intent。上述属性中,component属性为直接类型,其他均为间接类型。
相比与显式intent,隐式intnet则含蓄了许多,它并不明确指出我们想要启动哪一个活动,而是指定一系列更为抽象的action和category等信息,然后交由系统去分析这个intent,并帮我们找出合适的活动去启动。
activity 中 intent filter 的匹配过程 :
1、component(组件):目的组件
component属性明确指定intent的目标组件的类名称。(属于直接intent)
如果 component这个属性有指定的话,将直接使用它指定的组件。指定了这个属性以后,intent的其它所有属性都是可选的。
例如,启动第二个activity时,我们可以这样来写:
如果写的简单一点,监听事件onclick()方法里可以这样写:
再简单一点,可以这样写:(当然,也是最常见的写法)
2、action(动作):用来表现意图的行动
当日常生活中,描述一个意愿或愿望的时候,总是有一个动词在其中。比如:我想“做”三个俯卧撑;我要“写” 一封情书,等等。在intent中,action就是描述做、写等动作的,当你指明了一个action,执行者就会依照这个动作的指示,接受相关输入,表现对应行为,产生符合的输出。在intent类中,定义了一批量的动作,比如action_view,action_pick等, 基本涵盖了常用动作。加的动作越多,越精确。
action 是一个用户定义的字符串,用于描述一个 android 应用程序组件,一个 intent filter 可以包含多个 action。在 androidmanifest.xml 的activity 定义时,可以在其 <intent-filter >节点指定一个 action列表用于标识 activity 所能接受的“动作”。
3、category(类别):用来表现动作的类别
category属性也是作为<intent-filter>子元素来声明的。例如:
<intent-filter>
<action android:name="com.vince.intent.my_action"></action>
<category android:name="com.vince.intent.my_category"></category>
<category android:name="android.intent.category.default"></category>
</intent-filter>
action 和category通常是放在一起用的,所以这里一起介绍一下。我们来先来举一个例子:
新建一个工程文件smyh006_intent01,在默认文件的基础之上,新建文件secondacticity.java和activity_second.xml。
紧接着,我们要到清单文件中进行注册,打开androidmanifest.xml,添加secondactivity的action和category的过滤器:
上方代码,表示secondacticity可以匹配第4行的my_action这个动作,此时,如果在其他的acticity通过这个action的条件来查找,那secondacticity就具备了这个条件。类似于相亲时,我要求对方有哪些条件,然后对方这个secondacticity恰巧满足了这个条件(够通俗了吧)。
注:如果没有指定的category,则必须使用默认的default(即上方第5行代码)。
也就是说:只有<action>和<category>中的内容同时能够匹配上intent中指定的action和category时,这个活动才能响应intent。如果使用的是default这种默认的category,在稍后调用startactivity()方法的时候会自动将这个category添加到intent中。
现在来修改mainactivity.java中按钮的点击事件,代码如下:
上方代码中,也可以换成下面这种简洁的方式:
上方第5行代码:在这个intent中,我并没有指定具体哪一个activity,我只是指定了一个action的常量。所以说,隐式intent的作用就表现的淋漓尽致了。此时,点击mainacticity中的按钮,就会跳到secondacticity中去。
上述情况只有secondacticity匹配成功。如果有多个组件匹配成功,就会以对话框列表的方式让用户进行选择。我们来详细介绍一下:
我们新建文件thirdacticity.java和activity_third.xml,然后在清单文件androidmanifest.xml中添加thirdactivity的action和category的过滤器:
此时,运行程序,当点击mainactivity中的按钮时,弹出如下界面:
相信大家看到了这个界面,应该就一目了然了。于是我们可以做出如下总结:
在自定义动作时,使用activity组件时,必须添加一个默认的类别
具体的实现为:
<action android:name="com.example.action.my_action"/>
<category android:name="android.intent.category.default"/>
</intent-filter>
如果有多个组件被匹配成功,就会以对话框列表的方式让用户进行选择。
每个intent中只能指定一个action,但却能指定多个category;类别越多,动作越具体,意图越明确(类似于相亲时,给对方提了很多要求)。
目前我们的intent中只有一个默认的category,现在可以通过intent.addcategory()方法来实现。修改mainactivity中按钮的点击事件,代码如下:
既然在intent中增加了一个category,那么我们要在清单文件中去声明这个category,不然程序将无法运行。代码如下:
此时,点击mainacticity中的按钮,就会跳到secondacticity中去。
总结如下:
自定义类别: 在intent添加类别可以添加多个类别,那就要求被匹配的组件必须同时满足这多个类别,才能匹配成功。操作activity的时候,如果没有类别,须加上默认类别
4、data(数据):表示与动作要操纵的数据
data属性是android要访问的数据,和action和category声明方式相同,也是在<intent-filter>中。
多个组件匹配成功显示优先级高的; 相同显示列表。
data是用一个uri对象来表示的,uri代表数据的地址,属于一种标识符。通常情况下,我们使用action+data属性的组合来描述一个意图:做什么
使用隐式intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得android多个应用程序之间的功能共享成为了可能。比如应用程序中需要展示一个网页,没有必要自己去实现一个浏览器(事实上也不太可能),而是只需要条用系统的浏览器来打开这个网页就行了。
【实例】打开指定网页:
mainactivity.java中,监听器部分的核心代码如下:
当然,上方代码也可以简写成:
第4行代码:指定了intent的action是 intent.action_view,表示查看的意思,这是一个android系统内置的动作;
第5行代码:通过uri.parse()方法,将一个网址字符串解析成一个uri对象,再调用intent的setdata()方法将这个uri对象传递进去。
当点击按钮时,将跳到如下界面:
此时, 调用的是系统默认的浏览器,也就是说,只调用了这一个组件。现在如果有多个组件得到了匹配,应该是什么情况呢?
我们修改修改清单文件中对secondacivity的声明:
现在,secondactivity也匹配成功了,我们运行程序,点击mainacticity的按钮时,弹出如下界面供我们选择:
我们可以总结如下:
当intent匹配成功的组件有多个时,显示优先级高的组件,如果优先级相同,显示列表让用户自己选择
优先级从-1000至1000,并且其中一个必须为负的才有效
注:系统默认的浏览器并没有做出优先级声明,其优先级默认为正数。
优先级的配置如下:
在清单文件中修改对secondacivity的声明,即增加一行代码,通过来android:priority设置优先级,如下:
注:
data属性的声明中要指定访问数据的uri和mime类型。可以在<data>元素中通过一些属性来设置:
android:scheme、android:path、android:port、android:mimetype、android:host等,通过这些属性来对应一个典型的uri格式scheme://host:port/path。例如:http://www.google.com。
5、type(数据类型):对于data范例的描写
如果intent对象中既包含uri又包含type,那么,在<intent-filter>中也必须二者都包含才能通过测试。
type属性用于明确指定data属性的数据类型或mime类型,但是通常来说,当intent不指定data属性时,type属性才会起作用,否则android系统将会根据data属性值来分析数据的类型,所以无需指定type属性。
data和type属性一般只需要一个,通过setdata方法会把type属性设置为null,相反设置settype方法会把data设置为null,如果想要两个属性同时设置,要使用intent.setdataandtype()方法。
【任务】:data+type属性的使用
【实例】:播放指定路径的mp3文件。
具体如下:
新建工程文件smyh006_intent02,mainactivity.java中按钮监听事件部分的代码如下:
代码解释:
第6行:"file://"表示查找文件,后面再加上我的小米手机存储卡的路径:/storage/sdcard0,再加上具体歌曲的路径。
第8行:设置data+type属性
运行后,当点击按钮时,效果如下:
上方界面中,使用的是小米系统默认的音乐播放器。
6、extras(扩展信息):扩展信息
是其它所有附加信息的集合。使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个
动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。
7、flags(标志位):期望这个意图的运行模式
一个程序启动后系统会为这个程序分配一个task供其使用,另外同一个task里面可以拥有不同应用程序的activity。那么,同一个程序能不能拥有多个task?这就涉及到加载activity的启动模式,这个需要单独讲一下。
注:android中一组逻辑上在一起的activity被叫做task,自己认为可以理解成一个activity堆栈。
三、activity的启动模式:(面试注意)
activity有四种启动模式:standard、singletop、singletask、singleinstance。可以在androidmanifest.xml中activity标签的属性android:launchmode中设置该activity的加载模式。
standard模式:默认的模式,以这种模式加载时,每当启动一个新的活动,必定会构造一个新的activity实例放到返回栈(目标task)的栈顶,不管这个activity是否已经存在于返回栈中;
singletop模式:如果一个以singletop模式启动的activity的实例已经存在于返回桟的桟顶,那么再启动这个activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onnewintent()方法将intent对象传递到这个实例中;
注:如果以singletop模式启动的activity的一个实例已经存在于返回桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例;
singletask模式:这种模式下,每次启动一个activity时,系统首先会在返回栈中检查是否存在该活动的实例,如果存在,则直接使用该实例,并把这个活动之上的所有活动统统清除;如果没有发现就会创建一个新的活动实例;
singleinstance模式:总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重新调用已存在的任务和实例。并且会调用这个实例的onnewintent()方法,将intent实例传递到该实例中。和singletask相同,同一时刻在系统中只会存在一个这样的activity实例。(singleinstance即单实例)
注:前面三种模式中,每个应用程序都有自己的返回栈,同一个活动在不同的返回栈中入栈时,必然是创建了新的实例。而使用singleinstance模式可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪一个应用程序来访问这个活动,都公用同一个返回栈,也就解决了共享活动实例的问题。(此时可以实现任务之间的切换,而不是单独某个栈中的实例切换)
其实我们不在清单文件中设置,只在代码中通过flag来设置也是可以的,如下:
三、intent的常见应用:
1、打开指定网页:(直接复制的上面的代码)
或者可以写成:
2、打电话:
【方式一】打开拨打电话的界面:
运行程序后,点击按钮,显示如下界面:
【方式二】直接拨打电话:
要使用这个功能必须在配置文件中加入权限:(加一行代码)
3、发送短信:
【方式一】打开发送短信的界面:action+type
【方式二】打开发短信的界面(同时指定电话号码):action+data
4、播放指定路径音乐:action+data+type
5、卸载程序:action+data(例如点击按钮,卸载某个应用程序,根据包名来识别)
注:无论是安装还是卸载,应用程序是根据包名package来识别的。
6、安装程序:action+data+type
注:第2行的路径不能写成:"file:///storage/sdcard0/···",不然报错如下:
疑问:通过下面的这种方式安装程序,运行时为什么会出错呢?
综上所述,完整版代码如下:
mainactivity.java代码如下:
运行后,主界面如下: