Android 中 Intent 用法
Intent 是 Android 中用于组件间通信的信息对象,常用于启动组件和组件间传递参数以及应用程序之间的信息交互。Intent 作为一个负责组件间传递消息的信息对象,最重要的就是其包含的信息。实际上无论是显式还是隐式,Intent 发出的时候,系统对应的行为正是由 Intent 所包含信息的组合决定。一个 Intent 所包含的信息包括:Action、Data、Category、Flag、Extra、ComponentName等。
一、Intent 简介
Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般用于启动活动、启动服务以及发送广播等场景。
Intent 大致可以分为两种:显式 Intent 和隐式 Intent。
1.显式 Intent
如,在 FirstActivity 中打开 SecondActivity,代码如下:
Button button1=(Button)findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent =new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
}
});
2.隐式Intent
不明确指出我们想要启动哪一个活动,而是指定一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并帮我们找出合适的活动去启动。
如,在 FirstActivity 中打开 SecondActivity,用隐式的 intent 来实现上例中的效果,操作如下:
在AndroidManifest.xml中指定当前活动能响应的action和category。只有<action>和<category>中的内容同时匹配上 Intent 中指定的 action和category时,这个活动才能启动。
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Button button1=(Button)findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent=new Intent("com.example.activitytest.ACTION_START");
startActivity(intent);
}
});
这里 android.intent.category.DEFAULT 是一种默认的 category,在调用 startActivity() 时会自动将这个 category 添加到 Intent 中。
每个Intent中只能指定一个action,但却能指定多个category。如:
Button button1=(Button)findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent=new Intent("com.example.activitytest.ACTION_START");
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});
此时,你会发现程序崩溃,你可以查看 Logcat,实际上错误原因很简单,没有合适的活动能响应我们的 Intent。解决也很简单,如下:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
二、Intent的相关属性
Intent由以下各个组成部分:
- component(组件):目的组件
- action(动作):用来表现意图的行动
- category(类别):用来表现动作的类别
- data(数据):表示与动作要操纵的数据
- type(数据类型):对于data范例的描写
- extras(扩展信息):扩展信息
- Flags(标志位):期望这个意图的运行模式
Intent类型分为显式Intent(直接类型)、隐式Intent(间接类型)。官方建议使用隐式Intent。上述属性中,component属性为直接类型,其他均为间接类型。
1. component(组件):目的组件
Component 属性明确指定 Intent 的目标组件的类名称,指定后 Intent 仅传给指定的组件(属于直接 Intent,也就是所谓显性 Intent)。否则系统会根据其他信息筛选出可以响应 Intent 的所有组件(即隐性 Intent)。可以使用 setComponent()、setClass()、setClassName() 或 Intent 构造函数设置组件名称。(由于安全性原因,在5.0后的系统,启动 Service 时,应始终指定组件名称,否则会报错)
如果 component这个属性有指定的话,将直接使用它指定的组件。指定了这个属性以后,Intent的其它所有属性都是可选的。
例如,启动第二个Activity时,我们可以这样来写:
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//创建一个意图对象
Intent intent = new Intent();
//创建组件,通过组件来响应
ComponentName component = new ComponentName(MainActivity.this, SecondActivity.class);
intent.setComponent(component);
startActivity(intent);
}
});
如果写的简单一点,监听事件onClick()方法里可以这样写:
Intent intent = new Intent();
//setClass函数的第一个参数是一个Context对象
//Context是一个类,Activity是Context类的子类,也就是说,所有的Activity对象,都可以向上转型为Context对象
//setClass函数的第二个参数是一个Class对象,在当前场景下,应该传入需要被启动的Activity类的class对象
intent.setClass(MainActivity.this, SecondActivity.class);
startActivity(intent)
再简单一点,可以这样写:(当然,也是最常见的写法)
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
2、Action(动作):用来表现意图的行动
Action字段的含义表示其发向的组件需要执行的动作,例如:Action指示图片应用组件去展示图片,地图组件获取当前地址等。
- ACTION_VIEW:向用户展示某信息,例如:图片应用展示图片。
- ACTION_SEND:用于发送数据,例如:电子邮件应用发送邮件。
- ACTION_DIAL:显示带拨号盘的页面,让用户可以进行拨号动作。
3、category(类别):用来表现动作的类别
Action 和 category 通常是放在一起用的,如上面隐式 Intent 用法。只有<action>和<category>中的内容同时能够匹配上Intent中指定的action和category时,这个活动才能响应Intent。如果使用的是DEFAULT这种默认的category,在稍后调用 startActivity() 方法的时候会自动将这个 category 添加到Intent中。
常见的 Category:
- CATEGORY_BROWSABLE:目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。
- CATEGORY_LAUNCHER:该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。
需要注意的是大部分 Intent 虽然不需要设置 Category,但是在调用使用 Intent 的方法(如starActivity()等)的时候,会默认为该 Intent 添加 CATEGORY_DEFAULT,相应目标组件的 Intent 过滤器应该添加该类别。
Category属性也是作为 <intent-filter> 子元素来声明的。例如:
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"></action>
<category android:name="com.example.activitytest.MY_CATEGORY"></category>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
上述情况只有SecondActicity匹配成功。如果有多个组件匹配成功,就会以对话框列表的方式让用户进行选择。
我们新建文件ThirdActicity.java和activity_third.xml,然后在清单文件AndroidManifest.xml中添加ThirdActivity的action和category的过滤器:
<activity android:name=".ThirdActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
每个Intent中只能指定一个action,但却能指定多个category;类别越多,动作越具体,意图越明确。
目前我们的Intent中只有一个默认的category,现在可以通过intent.addCategory()方法来实现。修改MainActivity中按钮的点击事件,代码如下:
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//启动另一个Activity,(通过action属性进行查找)
Intent intent = new Intent();
//设置动作(实际action属性就是一个字符串标记而已)
intent.setAction("com.example.activitytest.ACTION_START"); //方法:Intent android.content.Intent.setAction(String action)
intent.addCategory("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});
既然在Intent中增加了一个category,那么我们要在清单文件中去声明这个category,不然程序将无法运行。代码如下:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
此时,点击MainActicity中的按钮,就会跳到SecondActicity中去。
因此,在Intent添加类别可以添加多个类别,那就要求被匹配的组件必须同时满足这多个类别,才能匹配成功。操作Activity的时候,如果没有类别,须加上默认类别
4、data(数据):表示与动作要操纵的数据
Data属性是Android要访问的数据,包含了 URI 对象和 memitype 两个部分,分别是待操作数据的引用 uri,以及待操作数据的数据类型。两部分均为可选,但是要注意同时设置时应该使用 setDataAndType()方法,防止互相抵消。和action和Category声明方式相同,也是在 <intent-filter> 中。多个组件匹配成功显示优先级高的;相同则显示选择列表。
Data是用一个uri对象来表示的,uri代表数据的地址,属于一种标识符。通常情况下,我们使用 action+data 属性的组合来描述一个意图:做什么
Data 内容一般由 action 决定,比如 action 为 ACTION_VIEW,那么 Data 就可以是一个网址,也可以是图片之类的数据 uri。同时指定 Uri 和 MIME 类型有助于 Android 系统找到接收 Intent 的最佳组件,例如可以响应 ACTION_VIEW 的组件可能有非常多,浏览器、播放器、图片应用等等。此时设置 mimeType 为 image/jpeg 或者 video/mp4,则系统可以筛选出更合适的响应组件。
使用隐式Intent,我们不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享成为了可能。比如应用程序中需要展示一个网页,只需要调用系统的浏览器来打开这个网页就行了。
实例:打开指定网页
代码如下:
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri data = Uri.parse("http://www.baidu.com");
intent.setData(data);
startActivity(intent);
}
});
当然,上方代码也可以简写成:
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
});
第4行代码:指定了Intent的action是 Intent.ACTION_VIEW,表示查看的意思,这是一个Android系统内置的动作;
第5行代码:通过Uri.parse()方法,将一个网址字符串解析成一个Uri对象,再调用intent的setData()方法将这个Uri对象传递进去。
此时, 调用的是系统默认的浏览器,也就是说,只调用了这一个组件。现在如果有多个组件得到了匹配,应该是什么情况呢?
我们修改修改清单文件中对SecondAcivity的声明:
<activity
android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="www.baidu.com"/>
</intent-filter>
</activity>
现在,SecondActivity也匹配成功了,我们运行程序,点击MainActicity的按钮时,弹出如下界面供我们选择。
当Intent匹配成功的组件有多个时,显示优先级高的组件,如果优先级相同,显示列表让用户自己选择
优先级从-1000至1000,并且其中一个必须为负的才有效
注:系统默认的浏览器并没有做出优先级声明,其优先级默认为正数。
优先级的配置如下:
在清单文件中修改对SecondAcivity的声明,即增加一行代码,通过来android:priority设置优先级,如下:
<activity
android:name=".SecondActivity">
<intent-filter android:priority="-1">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="www.baidu.com"/>
</intent-filter>
</activity>
注:
Data属性的声明中要指定访问数据的Uri和MIME类型。可以在元素中通过一些属性来设置:
android:scheme、android:path、android:port、android:mimeType、android:host等,通过这些属性来对应一个典型的Uri格式scheme://host:port/path。例如:http://www.google.com。
除了 http 协议,我们还可以使用其他协议,如 geo 表示显示地理位置、tel表示拨打电话等。如下:
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
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()方法。
实例:播放指定路径的mp3文件。
代码如下:
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri data = Uri.parse("file:///storage/sdcard0/平凡之路.mp3");
//设置data+type属性
intent.setDataAndType(data, "audio/mp3"); //方法:Intent android.content.Intent.setDataAndType(Uri data, String type)
startActivity(intent);
}
});
代码解释:
第6行:"file://"表示查找文件,后面再加上我的小米手机存储卡的路径:/storage/sdcard0,再加上具体歌曲的路径。
第8行:设置data+type属性
6、extras(扩展信息):扩展信息
表示 Intent 携带的附加数据,也是组件间相互传递信息的方式。使用各种 putExtra()方法添加 Bundle数据,每种方法均接受两个参数:键名和值。也可以通过创建一个包含所有 extra 数据的 Bundle 对象,然后使用 putExtras() 将 Bundle 插入 Intent 中。使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。
7、Flags(标志位):期望这个意图的运行模式
指示 Android 系统如何启动 Activity,例如,Activity 应属于哪个任务,以及启动之后如何处理。如:在Activity 跳转时用到 Intent Flag 可以设置新建 Activity 的创建方式。
一个程序启动后系统会为这个程序分配一个task供其使用,另外同一个task里面可以拥有不同应用程序的activity。
注:android中一组逻辑上在一起的activity被叫做task,自己认为可以理解成一个activity堆栈。