刘关张三人拿着孔明给的PopupWindow小册子琢磨了好一阵子,学习过PopupWindow之后,三人在Android开发上终于有了小成。而孔明每日只是浇浇花,唱唱歌,上上茅坑啥的,闲时回答回答三兄弟提出的各种问题,日子过得也十分逍遥。
这一日早上,刘备忽然提议说:“我们干学这些Android知识也不是办法,不如实践一下如何?”
关羽说:“大哥所言甚是,所谓学以致用,听闻村东头有个叫曹操的广结天下Android开发大牛,开什么Android英雄大会,我们不如过去凑凑热闹,看看有什么项目可做的?”
张飞说:“村东头?这么近?那还等什么?我们赶快去瞧瞧吧!”
刘备说:“可是军师仍在睡梦之中,我们不如等军师醒了再一起走?”
张飞不耐烦道:“唉,军师每天不是睡觉就是上茅房,等他出来的时候黄花菜都凉了,我们还是先去,搞到些项目回来也好跟军师炫耀一下,哈哈!”
刘备点了点头,说:“那我们现在就出发!”
于是三人手挽着手,屁颠屁颠的来到村东头。只见村东头一片空地上扎满了帐篷,一片人山人海,热闹非凡的景象。
“大哥,怎么有这么多人啊?”张飞诧异道。
刘备看了心也有点虚,说:“可能都是来凑热闹的,真正会开发的应该没几个吧?”
关羽说:“我们还是赶紧去瞧瞧。”
三人来到一个大帐篷前,只见一个书生打扮的人正在给人填表,刘备赶忙上去咨询,书生说:“我家主公一发英雄帖,各路Android开发大牛都云集此处,由于人员过多,所以我们必须筛选一下,给主公引荐真正的开发大牛。来这里填张表,然后等着叫你名参加面试。”
刘关张三人你看看我,我看看你,都心想,还要面试啊。但事已至此也只能先硬着头皮报名参加了。
当晚,孔明在家里翘着腿挖鼻孔,心想,那三大傻不知今天跑哪去了,今天连个被我吐槽的人都没有,好寂寞啊。没办法,只好继续挖鼻孔,正挖到兴高采烈,浑然忘我之际,见刘关张三人互相搀扶着走了进来。
孔明问:“你们这是?为何一副很受伤的样子?”
刘备二话不说冲过去抱住孔明的大腿,哭诉道:“军师啊,太欺负人啦,我们去曹操那里参加Android英雄大会,结果面试的时候被鄙视的七荤八素啊,骂得我好没尊严呀,呜呜呜,军师,我好委屈啊~”
孔明:“唉,别哭啦,鼻涕都擦到我裤子上了,就你们这点斤两就想去参加Android英雄大会,说说都问你们什么题目了吧?”
刘备说:“那考官让我好好解释一下Intent的用法和结构,我一紧张,啥叫Intent都忘了啊!”
张飞问:“是呀,Android有Intent这个东西吗?考官瞎编的吧?”
孔明说:“我晕啊……我说你们什么好,你们……没跟其他人说认识我吧?”
1.1.Intent简介
在第六回中,我们详细介绍了Activity的创建和使用。在解释如何解决多个Activity之间交互的问题时,多次提到了Intent。下面就让我们一起来看看Intent具体是什么。
Intent负责对应用程序中一次操作的动作、动作涉及的数据、及附加的数据内容进行描述。Android会根据此Intent的描述,与过滤器Intent-filter匹配,将Intent传递给相对应的组件,并完成组件的调用。Intent不仅可以应用于应用程序之间,也可用于应用程序内部Activity/Service之间的交互。
孔明:可以看出Intent在应用程序中,主要起着一个媒体和中介的作用,专门提供组件互相调用的相关信息。Intent实现调用者与被调用这之间的解耦。 张飞:恩,虽然不太懂,但感觉好强大啊…. 孔明:唉,就打个比方,老关、老张,你们一起去饭馆吃饭。你点了羊肉串,老关点了炒饭,这就是你们的Intent。然后,卖羊肉串的就开始烤串,厨师就炒饭。这样通过菜单,就实现了调用者与被调用者的解耦。 张飞:我懂了,卖羊肉串的intent-filter就是他会烤肉串、鸡翅、扇贝、板筋、肉筋、玉米、馒头片…… 刘备:呃……咱还是吃饭去吧。 |
1.2. Intent的属性设置
Intent对象是一些信息的集合。它包含接收这个Intent的组件要使用的信息和一些Andoird系统要用到的信息。Intent主要有以下几个属性:
componentname属性
componentname表示Intent组件的名称。一个componentname对象,由两部分组成。第一部分是包名,注意一定要和Manifest中设置的包名相匹配,如“com.firstpeople.example.intent”;第二部分则是类名,要包含前面的包名,如“com.firstpeople.example.intent.HomeActivity”。component并不是必须的,通常Android会根据Intent中包含的其他属性的信息,例如action、data/type、category进行查找,并最终找到一个与之匹配的目标组件。但是如果指定了component,则不再执行上述的查找过程,直接使用它指定的组件。指定了这个属性之后,Intent的其他属性都是可选的。
action属性
action属性是一个字符串类型,表示该Intent要执行的动作或者广播broadcast中要报告的事件。Android在Intent类中,定义了一些标准action常量,如表9-1所示:
表9-1 action常量
常量 | 目标组件 | 说明 |
ACTION_CALL | Activity | 直接拨打电话。 |
ACTION_EDIT | Activity | 显示用户编辑的数据。 |
ACTION_MAIN | Activity | 标识该Activity作为一个程序的开始。 |
ACTION_SYNC | Activity | 同步手机与数据服务器上的数据。 |
ACTION_BATTERY_LOW | Broadcast Receiver | 电池电量过低警告。 |
ACTION_HEADESET_PLUG | Broadcast Receiver | 插拔耳机警告。 |
ACTION_SCREEN_ON | Broadcast Receiver | 屏幕变亮警告。 |
ACTION_TIMEZONE_CHANGED | Broadcast Receiver | 改变时区警告。 |
data属性
data属性是执行动作要操作的数据,Android中采用指向数据的一个URI来表示,不同的action应对应不同的data。例如,如果action是ACTION_EDIT,data中应该包含一个含有要显示文字的URI,如果action是ACTION_CALL,data应该是tel:URI这种形式。在将一个Intent与component相匹配的时候,知道data的URI中的MIME类型非常重要。一个只能显示图片的组件,不应用来处理音频数据。
张飞:二哥,啥是MIME数据类型啊? 关羽:MIME是指多用途互联网邮件扩充服务(Multipurpose Internet Mail Extensions)。在Android中通过文件的MIME类型来判断有哪些应用程序可以处理这些文件。 张飞:…… 关羽:就是一个字符串,指定了数据的类型,使Android能够识别。一般以[type]/[subtype]的形式出现,比如mp3文件,它的MIME type就是audio/x-mpeg,明白啦? 刘备:三弟,要淡定! |
一般情况下,data的type可以从data的URI中获得,但是type也可以通过setType()或者setDataAndType方法明确指定,例如:
Uri uri =Uri.parse("file:///sdcard/song.mp3");
intent.setDataAndType(uri," audio/x-mpeg");
category属性
category属性表示要执行action的类别。一个Intent可以有多个category。在Intent中定义了category常量,如表9-2所示:
表9-2 category常量
常量名 | 说明 |
CATEGORY_BROWSABLE | 当Activity可以安全的被浏览器启动,用于显示一个连接中的数据,例如一个图片,或者一封email时,皆可以指定为BROWSABLE。 |
CATEGORY_GADGET | 标识该Activity可以嵌入到另一个activity中。 |
CATEGORY_HOME | 标识该Activity用于显示Home界面,也就是用户按下Home按钮后出现的界面。 |
CATEGORY_LAUNCHER | 在启动程序时,让这个Activity出现在顶层。 |
CATEGORY_ALTERNATIVE | 表示这个action应该包含在对某种数据的执行动作列表中。例如,操作一个联系人动作可以是浏览它,也可能是去编辑或删除它。 |
可以通过以下三个方法对category进行操作:addCategory()增加一个类别,removeCategory()删除一个类别,getCategories()获得支持的类别。
extras属性
extras属性指其他所有附加信息的集合。使用extras可以为组件提供扩展信息。就像有些action有其特定的dataURI一样,某些action也有其对应的extras。例如,要执行“发送电子邮件”这个动作,可以把电子邮件的标题、正文等分别保存在extras不同的键值当中。extra属性可以通过一系列put…()和get…()方法进行写入和读取,也可以使用Bundle类型进行数据传递,附加数据可以通过intent.putExtras()和intent.getExtras()进行传递。
张飞:Bundle是啥? 孔明:Bundle就是Android的一种key-value对,可以用来存储各种数据,例如,可以用(string, int)的形式存储int。 |
flags属性
flags属性用于指定Activity的启动方式和启动后系统对Activity的处理方式,在Intent类中定义的flags有20多个,常用如表9-3所示:
表9-3 flags常量
常量名 | 说明 |
FLAG_ACTIVITY_BROUGHT_TO_FONT | 表示如果Activity在task中存在,就拿到最顶端,并不启动新的Activity。 |
FLAG_ACTIVIEY_CLEAR_TOP | 如果Activity在task中存在,就将Activity之上所有的Activity清除掉。 |
FLAG_ACTIVITY_NEW_TASK | 将Activity放到一个新的task中。 |
FLAG_ACTIVITY_NO_HISTORY | 新启动的Activity将不会保存在History Stack中。 |
1.3. Intent的解析
这一节将对Intent的原理进行解析。需要说明的是,Intent根据是否指定了目标组件分为以下两类:
显式的Intent
在构造Intent时就设置component属性,直接指定Intent的接收者。由于应用程序的开发者一般不会获得另一应用程序的组件名,所以显式的Intent常用于应用程序内部的消息传递,例如,在程序内部开启一个新的Activity。对于显式的Intent,因为已经明确指定了目标组件,Android不需要再去做解析。
隐式的Intent
Intent发送者,在发送Intent时不知道,也不关心接收者是谁(component是缺省的)。隐式的Intent常用于应用程序之间的消息传递,这样做有利于降低发送者与接收者之间的耦合。Android需要解析那些隐式的Intent。通过用Intent的action、data(URI与type)和Category与组件的intent-filter进行比对,Android最终将Intent传递给可以处理此Intent的Activity、Broadcast Receier或者Service。Intent的extras与flags字段并不参与上述过程。
1.3.1.intent-filter
intent-filter不仅可以在XML中规定,也可以在代码中实现。因为Android系统在组件启动前就需要获得这个组件的intent-filter,所以intent-filter一般是在应用程序的manifast中以<intent-filter>元素的方式进行声明。如果组件不包含任何intent-filter,那么它只能接收显式的Intent。含有intent-filter的组件既可以接收显式的Intent也可以接收隐式的Intent。
Activity,service和broadcast receiver有时会声明一个甚至多个intent-filter,每个intent-filter都对组件进行了描述。例如,在Android自带的Note pad示例中,NoteEditor Activity声明了两个intent-filter,一个启动并显示一个特定的记录给用户查看或者编辑,另一个启动一个空的记录给用户编辑。
多个intent-filter中,只要通过了一个intent-filter就可以将Intent投递到组件。下面将具体介绍Intent的匹配规则。
1.3.2.action 匹配
如果Intent指定了action,则目标组件的intent-filter的action列表中必须包含有该action,否则不能匹配。下面通过一段代码介绍如何匹配action,如下所示:
action匹配代码清单9-3-2:
<intent-filter>
……
<actionandroid:name="android.intent.action.MAIN" /> //action MAIN标示程序入口
<actionandroid:name="com.firstpeople.example.intent.SHOW_CURRNET" />
<actionandroid:name="com.firstpeople.example.intent.SHOW_LAST" />
……
</intent-filter>
由上述代码所示,一个intent-filter可以列出多个action,而一个Intent对象只能指定一个action。如果一个intent-filter没有<action>元素,它将阻止所有Intent通过。另一方面,如果一个Intent没有指定action,它可以与任何含有<action>元素的intent-filter匹配
张飞:啊啊啊啊,这些action,老夫怎么都没见过? 关羽:“android.intent.action.MAIN”是前面提到的ACTION_MAIN的全拼,而其他两个则是自定义action。理论上任何字符串,都可以作为action,但是自定义action最好以应用程序的包名作为开头。 张飞:怎么不早说…. |
1.3.3.category 匹配
如果Intent指定了一个或多个category,这些category必须全部出现在intent-filter的category列表中,否则不能匹配。下面通过一段代码介绍如何匹配category,如下所示:
category匹配代码清单9-3-3:
<intent-filter>
……
<categroyandroid:name="android.intent.category.DEFAULT" />
<categroy android:name="android.intent.category.ALTERNATIVE" />
……
</intent-filter>
如果一个Intent没有指定category,那么该Intent总能通过category测试。需要注意的是,Android认为所有隐式的Intent都默认含有一个category,即“android.intent.category.DEFAULT”,因此所有希望接收隐式Intent的组件都应在其intent-filter内包含“android.intent.category.DEFAULT”。
1.3.4.data 匹配
类似于action和category,data也是intent-filter中的子节点,可以设置多个data节点,也可以一个都不设置。data的匹配至关重要,因为这说明了该组件支持的数据格式,例如,一个只支持图片查看的应用需要将含有音乐格式数据的Intent过滤掉。下面通过一段代码介绍如何匹配data,如下所示:
data匹配代码清单9-3-4:
<intent-filter>
……
<dataandroid:mimeType="video/mpeg" android:scheme=”content”……/>
<dataandroid:mimeType="audio/*”android:sheme=”http”……/>
……
</intent-filter>
每个data元素如下所示:
data节点代码清单9-3-4所示:
<data
android:mimetype=”string”
android:scheme=”string”
android:host=”string”
android:port=”string”
android:path=”string”
android:pathPattern=”string”
android:pathPrefix=”string”/>
data节点可以指定一个mimeType属性和一些URI属性,如下所示:
mimetype属性
这个属性指定了数据的MIME类型。其子类型可用星号通配符(*)来代替,表示能够与任何子类型匹配,例如,“audio/*”。
URI属性
l scheme:用于指定URI中的scheme部分。至少要给intent-filter设置一个scheme属性,否则其他的URI属性就都没有意义了。需要注意的是,scheme匹配时是大小写敏感的,要始终使用小写字母来指定scheme。
l host:这个属性定义了URI的主机部分,除非给出scheme属性,否则这个属性没有意义。
l port:这个属性定义了URI的端口部分,只有当intent-filter指定了scheme和host属性时该属性才有意义。host与port一起构成了URI的authority。
l path、pathPrefix、pathPattern:path属性指定一个完整的路径,这个路径会和Intent对象中的路径进行匹配。pathPrefix属性指定了部分路径,它会跟Intent对象中路径的初始部分进行匹配。pathPattern属性也要和Intent路径进行完整匹配,但是可以包含 (*)和(.*)通配符。
在intent-filter中这些属性都是可选的,同时也是相互依赖的。如果没有给指定scheme属性,那么所有其他URI属性都将被忽略;如果没有指定host属性,那么port属性和所有path属性也将被忽略。
Intent对象中的URI将按“scheme://host:port/path or pathPrefixor pathPattern”的格式解析成几个独立属性,例如:“content://com.firstpeople.example.intent:200/folder/subfolder/content”的属性是:
l schema=”content:”
l host=”com.firstpeople.example.intent”
l port=”200”
l path=” folder/subfolder/content”
包含在同一个<intent-filter>元素中所有的<data>节点只会对该intent-filter起作用,例如:
<intent-filter>
……
<dataandroid:mimeType="video/mpeg" android:scheme=”content”/>
……
</intent-filter>
和
<intent-filter>
……
<dataandroid:mimeType="video/mpeg" />
<data android:sheme=”content”/>
……
</intent-filter>
二者的效果是相同的。
URI匹配规则
当Intent对象的URI和intent-filter中的URI进行匹配时,只对列在intent-filter中的属性进行比较。例如:如果一个intent-filter只指定了一个scheme,那么所有包含该scheme的URI的Intent都能匹配。如果一个intent-filter指定了scheme和authority,没有指定path,那么所有包含此scheme和authority的Intent都认为是匹配的。
孔明:URI匹配在实际开发中,不如mimeType用途广泛,大家了解即可。
|
data匹配规则
Android会对Intent对象中的URI和datatype与intent-filter指定的URI和datatype进行比较,比较规则如下:
l 如果Intent没有指定URI和datatype,那么只有当intent-filter也未指定URI和datatype时,才能匹配。
l 如果Intent只有URI,没有data type(且datatype不能从URI中推断出来),那么只有当filter的URI与Intent的URI相匹配,且filter中并未指定mimeType时,才认为是匹配的。这种情况只限于不指向实际数据的URI,例如:“tel:55555”或“mailto:abc@gmail.com”,换句话说,这些URI本身就是数据,而不是指向数据的地址。
l 如果Intent只有datatype没有URI,那么只有当intent-filter中列出相同的data type且没有指定URI时,才能匹配。
l 如果Intent既有datatype或者data type可以从URI中推断出来,也有URI,那么匹配时存在两种情况:(1)data type必须要与intent-filter中列出的data type相匹配,且URI也必须匹配,这才认为Intent与intent-filter是匹配。(2)如果data的URI是“content:”或者“file:”,intent-filter也没有指定scheme,且data type与intent-filter中的data type匹配,这时才认为Intent与intent-filter是匹配。因为对Android来说content和file这两种scheme是最常见的,就被当成缺省值来对待。
需要注意的是如果一个Intent可以被多个Activity或者Service匹配,用户可能会被询问要使用哪个组件;如果没有找到任何一个组件,则会抛出异常。
1.3.5.intent-filter实例
在写一个intent-filter作为一个系统的公共接口时有一些注意事项,如下所示:
intent-filter代码清单9-3-5:
<!--小飞飞:低调!才是最牛的炫耀!!-->
<intent-filter>
<!--选择一个Android自带的action-->
<action android:name="android.intent.action.SEND"/>
<!--一定要添加default category -->
<categoryandroid:name="android.intent.category.DEFAULT" />
<!--指定data type -->
<dataandroid:mimeType="image/*" />
<!--如果需要的话,指定data的来源 -->
<dataandroid:shceme="http" />
</intent-filter>
下面将要介绍Service和Broadcast Receiver的intent-filter配置。
音乐播放器Service的intent-filter代码清单9-3-5:
<!--小羽羽:我只管好我自己的码,别人的码跟我没有任何关系。-->
<intent-filter>
<actionandroid:name="android.intent.action.VIEW" />
<!-- 增加default category,接收隐式的intent -->
<category android:name="android.intent.category.DEFAULT"/>
<!--可以处理所有的audio类型-->
<dataandroid:mimeType="audio/*" />
</intent-filter>
该段代码使用了缺省的data URI属性,仍然可以接收URI是“content:”或者“file:”类型的Intent,使播放器支持播放本地文件。
处理SD卡状态变化的BroadcastReceiver的intent-filter代码清单9-3-5:
<!--小备备:好马不安二鞍,烈女不编代码。-->
<intent-filter>
<actionandroid:name="android.intent.action.MEDIA_MOUNTED"/>
<actionandroid:name="android.intent.action.MEDIA_UNMOUNTED"/>
<actionandroid:name="android.intent.action.MEDIA_SHARED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<actionandroid:name="android.intent.action.MEDIA_EJECT"/>
<categoryandroid:name="android.intent.category.DEFAULT" />
<dataandroid:scheme="file" />
</intent-filter>
该段代码指定了data,但是并未指定data type,与上述data匹配规则所述情况2一致。
1.4. 使用Intent
前面介绍了Intent的基本原理,本节将要详细介绍Intent的使用方法。
1.4.1.Intent的使用方式
Intent主要有以下几种使用方式:
l 通过Context.startActivity()或者Activity.startActivityForResult()启动一个新的Activity,或者让一个已存在的Activity做一些新的操作。
l 通过Context.startService()启动一个服务或者通过Context.bindService与后台服务交互。
l 通过广播如Context.sendBroadcast()或Context.sendOrderedBroadCast()方法发送给Broadcast receiver。
1.4.2.使用显式的Intent
正向前面所说的,显式的Intent一般用于在程序内部启动Activity。于是张飞写了个小程序来描述他的一天,顺便验证一下学习成果。
HomeActivity.java代码清单9-4-2 :
/**
* 使用显式的Intent
* ——记我的一天
* @author张飞:因为我要活着,不吃怎么行啊?人生啊,总是会碰到这样那样的烦心事,大
吃一顿也是一天,小吃一顿也是一天,就让自己大吃一天吧。
*/
publicclass HomeActivity extends Activity implements OnClickListener {
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.homelayout);
Button button =(Button)findViewById(R.id. restaurantButton);
//设置监听器
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//点击吃饭按钮
case R.id.restaurantButton:
//构造intent
Intent intent = new Intent();
//设置组件名称
intent.setComponent(newComponentName(getApplication(), RestaurantActivity.class));
//启动餐馆activity
startActivity(intent);
break;
default:
break;
}
}
}
图9-1 显式的Intent实例
点击“吃饭”按钮,就会启动RestaurantActivity,如下图9-2所示:
图9-2 RestaurantActivity的界面一
Android设置组件名称的方法有:
publicIntent setComponent(ComponentName component);
publicIntent setClass(Context packageContext, Class<?> cls);
publicIntent setClassName (Context packageContext, String className);
public Intent setClassName (StringpackageName, String className);
实际上,虽然有这么多的设置方法,但是在Intent内部标识组件名称的只有一个component,有这么多方法的原因是component有多种重载的构造函数。在程序内部启动Activity设置Intent如下所示:
显式的Intent设置代码清单9-4-2:
Intentintent = new Intent();
intent.setClass(getApplication(),NewActivity.class);
intent.setClassName(getApplication(),NewActivity.class.getName());
intent.setClassName(getApplication().getPackageName(),NewActivity.class.getName());
intent.setComponent(newComponentName(getApplication(), NewActivity.class));
intent.setComponent(new
ComponentName(getApplication(),NewActivity.class.getName()));
intent.setComponent(newComponentName(getApplication().getPackageName(),NewActivity.class.getName()));
startActivity(intent);
需要注意的是为避免拼写错误,在设置Intent时,因尽量避免直接使用字符串常量作为参数。
1.4.3.使用自定义action
又经过一番奋战,张飞终于完善了点餐系统,它通过自定义action来实现程序内部的跳转,并使用extra传递附加信息,代码如下所示:
RestaurantActivity.java代码清单9-4-3:
/**
* 使用自定义action
* ——蚝情壮翅点餐系统
* @author张飞:因为我要写代码着,不吃怎么行啊?人生啊,总是会碰到这样那样的烦心事,
吃烤鸡翅也是一顿,吃小白菜也是一顿,就让自己吃烤鸡翅吧。
*/
public class RestaurantActivity extendsActivity implements OnClickListener {
// 泡面的蛮文原来是这么写的,受教了!——刘备
RadioButton intentNoodlesRadio = null;
RadioButton chickenWingRadio = null;
Button okButton = null;
EditText requestEditor = null;
//烤串和泡面的自定义action
static final String ACTION_BARBECUE =
"com.firstpeople.examples.intent.explicit.ACTION_BARBECUE";
static final String ACTION_NOODLE =
"com.firstpeople.examples.intent.explicit.ACTION_NOODLE";
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.restaurantlayout);
intentNoodlesRadio = (RadioButton)findViewById(R.id.instantNoodlesRadio);
chickenWingRadio= (RadioButton)findViewById(R.id.checkenWingRadio);
okButton= (Button)findViewById(R.id.okButton);
requestEditor= (EditText)findViewById(R.id.requestEdit);
//设置监听器
okButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//点击点菜按钮
case R.id.okButton:
Intentintent = new Intent();
//将顾客的特殊要求放入Extra中,key设为“request”
intent.putExtra("request",requestEditor.getText().toString());
//如果选择鸡翅
if(chickenWingRadio.isChecked()){
intent.setAction(ACTION_BARBECUE);
}
//如果选择泡面
else{
intent.setAction(ACTION_NOODLE);
}
//使用隐式intent启动Activity
startActivity(intent);
break;
default:
break;
}
};
}
运行程序,结果如图9-3所示:
图9-3 RestaurantActivity的界面二
如图9-3所示,张飞的要求是“越辣越好”,点击“点菜”按钮后,将使用隐式的Intent启动BarbequeActivity。为了能正确获得Intent,需要在manifast中为BarbequeActivity设置intent-filter,如下代码所示:
设置intent-filter代码清单9-4-3:
<activityandroid:name=".BarbecueActivity"android:label="@string/restaurant_name">
<intent-filter>
<actionandroid:name="com.firstpeople.examples.intent.explicit.ACTION_BARBECUE"/>
<!--一定要有category.DEFAULT -->
<categoryandroid:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
孔明:需要注意的是,intent-filter里面的action字符串常量千万不要写错,因为这个在编译时不会被检查,运行时又没有异常,查找bug就会很麻烦。
|
BarbecueActivity.java代码清单9-4-3:
/**
* BarbecueActivity
* ——我爱Barbecue
* @author小飞飞:红烧鸡翅膀我喜欢吃!
小明明:但是你老娘说你快升天!
小飞飞:越快升天所以越要更多吃,如果现在不吃以后没机会再吃!
小明明:你真的快升天?
小飞飞:我真的快升天!
合:如果现在不吃以后没机会再吃!
*/
public class BarbecueActivity extendsActivity {
TextView barbecueTextView = null;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.barbecuelayout);
barbecueTextView =(TextView)findViewById(R.id.barbecueTextView);
//获得Extra中存储的信息,
String fromExtra =getIntent().getExtras().getString("request");
barbecueTextView.append(fromExtra);
};
}
运行程序,结果如图9-4所示:
图9-4 Barbeque界面
可以出,该代码通过设置Intent的extra属性成功地将张飞的要求传到了BarbequeActivity。
刘备:在一个Activity中,我们可以通过getIntent()方法获得启动该Activity的Intent。
|
1.4.4.使用Intent调用系统组件
为丰富生活,张飞对“我的一天”进行了增强,使用Intent直接调用呼叫组件。当张飞点击图9-1中的“打逗逗”按钮就可以直接给逗逗打电话了,如下代码所示:
HomeActivity.java的onClick()代码清单9-4-4:
/**
* @author 小飞飞:大哥,你别偷吃我的鸡翅!做码农要厚道!
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
//点击吃饭按钮
case R.id.restaurantButton:
Intent intent = new Intent();
//尽量以这种方式指定Component,避免拼写出错
intent.setComponent(newComponentName(getApplication(), RestaurantActivity.class));
startActivity(intent);
break;
case R.id.punchDouDouButton:
//将电话直接写入data的URI中
Uri uri = Uri.parse("tel://123456");
//隐式构造intent
Intent it = newIntent(Intent.ACTION_CALL, uri);
//呼叫逗逗
startActivity(it);
break;
default:
break;
}
}
图9-5 “打逗逗”界面
将Intent的Action设置为为Intent.ACTION_CALL就可以直接将电话拨打出去,但需要在mainfast中增加permission:"android.permission.CALL_PHONE"。下面列出了几个常用的action供大家参考:
浏览网页
Uri uri =Uri.parse("http://www.google.com");
Intent it = new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);
打开录音机
Intent it = newIntent(Media.RECORD_SOUND_ACTION);
startActivity(it);
打开联系人列表
Intent i = new Intent();
i.setAction(Intent.ACTION_GET_CONTENT);
i.setType("vnd.android.cursor.item/phone");
startActivityForResult(i, REQUEST_TEXT);
1.4.5.使程序成为公共接口
刘备看了张飞的程序深受启发,决定自己也写个程序。通过让程序成为系统的公共接口使其可以直接发送编辑好的短信。
CustomSms.java代码清单9-4-5:
/**
* @author 小备备:曾经有一份真挚的鸡翅膀放在我面前,我没有珍惜,等被张飞吃掉后我才后
悔莫及,人世间最痛苦的事莫过于此。如果上天能够给我一个再吃一次的机
会,我会对那个鸡翅膀说三个字:我要吃。如果非要在这句话上加上一个期
限,我希望是……吃一万年!
*/
public class CustomSms extendsActivity implements OnClickListener {
Button smsButton = null;
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
smsButton = (Button)findViewById(R.id.buttonSendSms);
smsButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonSendSms:
//获得启动本Activity的URI,例如"smsto:number"
Uriuri = getIntent().getData();
//使用ACTION_VIEW启动短信程序,避免再次选择程序
Intentit = new Intent(Intent.ACTION_VIEW, uri);
//使用getSchemeSpecificpart()获得smsto:之后的number
Stringnumber = uri.getSchemeSpecificPart();
//发送短信使用ACTION_VIEW的方式时,对Data的URI解析有误
//需要将号码直接用address的方式传入Intent,否则将不会显示联系人
it.putExtra("address",number);
//这句千万不能忘!——刘备
it.putExtra("sms_body","今天该你请客了,呵呵");
//进入短信发送界面
startActivity(it);
break;
default:
break;
}
}
}
运行程序,结果如图9-7所示:
图9-6 CustomSms界面
图9-7 选择程序界面
点击“发送蹭饭短信”按钮,就可以直接发送预先写好的短信,如图9-8所示:
图9-8 短信发送界面
为使这个程序能够获得发送短信的Intent,在程序的manifest中增加intent-filter如下:
CustomSms的intent-filter代码清单9-4-5:
<intent-filter>
<actionandroid:name="android.intent.action.SENDTO" />
<dataandroid:scheme="smsto" />
<categoryandroid:name="android.intent.category.DEFAULT" />
</intent-filter>
与发送短信类似,通过增加相应的intent-filter,可以对系统中产生的各种Intent进行过滤:
对HTTP请求的Intent进行过滤
<intent-filter>
<actionandroid:name="android.intent.action.VIEW" />
<dataandroid:scheme="http" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
过滤图片分享Intent
<intent-filter>
<actionandroid:name="android.intent.action.SEND" />
<dataandroid:mimeType="image/*" />
<categoryandroid:name="android.intent.category.DEFAULT" />
</intent-filter>
对通话的Intent进行过滤
<intent-filter >
<actionandroid:name="android.intent.action.CALL_PRIVILEGED" />
<categoryandroid:name="android.intent.category.DEFAULT" />
<dataandroid:scheme="tel" />
</intent-filter>
1.5. 玄德有话说
张飞:如果有两个Activity,从Activity1跳转到Activity2,那么还能从Activity2回到Activity1么?
刘备:当然可以,在Activity2中使用startActivity()不就行了么?
孔明:这个做法不太规范。还有更好的方法,在Activity2中直接使用startActivityForResult(),就行了。
张飞:什么意思?
孔明:在Activity2中,setResult()之后调用finish()结束Activity,在Activity1中使用onActivityResult()进行监听,就行了。
刘备:我听说intent-filter还有一个priority属性,是怎么回事?
孔明:这个会决定BroadCastReceiver接收广播的顺序,具体在后面章节会有介绍
张飞:我的程序为什么收不到Intent?
孔明:Android默认所有隐式的Intent都包含默认类别,你是否加了<categoryandroid:name="android.intent.category.DEFAULT" />?