5.1.Intent的Action、Category属性
5.1.1.概述
前面介绍了如何通过在Intent中设置目标类.class,启动Activity、Service。Android为Intent启动组件还提供了第二种方式:
通过在Action属性或Category属性中设置具有一定含义的字符串信息。
然后在项目清单文件中注册Activity类时,通过<intent-filter>标签来设置与上一步中相同的action或category字符串。从而启动符合条件的Activity。
5.1.2.Action和Category属性的设置
●Action属性:字符串类型,在该属性中可通过一个字符串来表示启动窗口时符合的“动作”。
●category属性:字符串类型,在该属性中可通过一个字符串来表示启动窗口时符合的类别。
5.1.3.示例代码
假设有两个Activity,一个是MainAct(项目入口),一个是SecondAct。
Intent intent=new Intent();
Intent.setAction(“com.tarena.exer05_1.SECOND_ACTIVITY”);
startActivity(intent);
以上代码在Intent中没有指明目标Activity,而是在Action属性中设置了一个字符串:
com.tarena.SECOND_ACTIVITY,那么在项目清单文件中注册SecondAct类时,需要在<intent-filter>标签中做一定的设置,从而达到启动SecondAct的目的。
提示:以上的Action中的字符串可以是任意的字符,原则是包名+类名的大写形式,避免重复。
5.1.4.预定义Action和Category属性值
Action和category的值可以自定义,Android系统也提供了许多预定义的常量值,用于启动系统预定义的Activity、Service。
Intent类中与Action相关的常量列表
Action常量 |
对应的字符串 |
说明 |
ACTION_MIAN |
android.intent.action.MAIN |
应用程序入口 |
ACTION_VIEW |
android.intent.action.VIEW |
显示指定数据 |
ACTION_EDIT |
android.intent.action.EDIT |
编辑指定数据 |
ACTION_DIAL |
android.intent.action.DIAL |
显示拨号面板 |
ACTION_CALL |
android.intent.action.CALL |
向指定用户打电话 |
ACTION_SEND |
android.intent.action.SEND |
向其他人发送数据 |
ACTION_SENDTO |
android.intent.action.MESSAGE |
向其他人发送消息 |
ACTION_ANSWER |
android.intent.action.ANSWER |
应答电话 |
ACTION_INSERT |
android.intent.action.INSERT |
插入数据 |
ACTION_DELETE |
android.intent.action.DELETE |
删除数据 |
ACTION_RUN |
android.intent.action.RUN |
运行数据 |
ACTION_SYNC |
android.intent.action.SYNC |
用户数据同步 |
ACTION_PICK_ACTIVITY |
android.intent.action.PICK_ACTIVITY |
选择Activity |
ACTION_SEARCH |
android.intent.action.SEARCH |
执行搜索 |
ACTION_WEB_SEARCH |
android.intent.action.WEB_SEARCH |
执行Web搜索 |
图-1
Intent类中与Category相关的常量值列表
Category常量 |
对应的字符串 |
说明 |
CATEGORY_DEFAULT |
android.intent.category.DEFAULT |
默认的Category |
CATEGORY_TAB |
android.intent.category.TAB |
指定Activity作为TabActivity的Tab页 |
CATEGORY_LAUNCHER |
android.intent.category.LAUNCHER |
Activity显示在*程序列表中 |
CATEGORY_INFO |
android.intent.category.INFO |
用于提供包信息 |
CATEGORY_HOME |
android.intent.category.HOME |
设置该Activity随系统启动而运行 |
CATEGORY_PREFERENCE |
android.intent.category.PREFERENCE |
设置Activity是参数面板 |
图-2
5.2.<intent-filter>标签
5.2.1.概述
以上的启动还差一个环节:在项目清单中注册组件时,在<intent-filter>标签中设置<action>标签和<category>标签的值。
<intent-filter>是Intent的过滤器,在该过滤器中通过在<action>、<category>标签中设置条件,凡符合设置条件的Activity都会被启动。
5.2.2.通过Action和Category启动Activity
假设有一个名为SecondAct.java的类,项目入口为:MainAct,按以下步骤启动SecondAct:
步骤1、在项目清单文件中注册SecondAct类。
<activity android:name=".SecondAct">
<intent-filter>
<action android:name="com.tarena.exer05_1.SECOND_ACTIVITY" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
步骤2、在MainAct的onCreate()方法中输入以下代码:
Intent intent=new Intent();
Intent.setAction(“com.tarena.exer05_1.SECOND_ACTIVITY”);
startActivity(intent);
提示:
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
以上两行是项目启动的Activity的<action>和<category>的值(对照图-1和图-2可知这两个属性值的作用。
对于不是入口的Activity,其<category>值通常按以下代码设置:
<category android:name="android.intent.category.DEFAULT" />
5.2.3.通过Action和Category启动Service
Service也可以用上述方式启动,示例如下:
假设有一个名为Myservice.java的服务,项目入口为:MainAct,按以下步骤启动MyService:
步骤1、按以下代码所示在项目清单文件中注册该服务:
<service android:name="MyService">
<intent-filter>
<action android:name="com.tarena.exer05_2.MY_SERVICE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
步骤2、在MainAct类的onCreate()方法按以下代码启动MyService:
Intent intent=new Intent();
intent.setAction("com.tarena.exer05_2.MY_SERVICE");
startService(intent);
5.3.BroadcastReciver
5.3.1.概述
Android系统提供了广播(Broadcast)的机制,该机制模拟广播电台的广播机制,首先由一方发送广播。其次定义一个BroadcastReceiver(广播接收者)对象,该对象用来接收发送的广播。
Android用字符串模拟广播电台的频率,当出现一个广播时,同时在Intent对象中设置Action字符串,该字符串可看作是该广播的频率。
广播接收者对象在定义后需要象Activity和Service那样在项目清单文件中注册,注册的方式与Activity和Service相同,在<intent-filter>标签中设置<action>的属性值与发送的广播的intent.action的字符串值相同。这样发送和接收者通过action属性就在处在同一“频段”上了。如同收音机只要将波段调至某个频段,就能接收该频段的广播一样。
5.3.2.注册BroadcastReceiver
注册BroadcastReceiver有两种方式。
5.3.2.1.在项目清单文件中注册
示例代码:
<receiver android:name=".ReceiverAndroid">
<intent-filter>
<action android:name="com.tarena.exer05_3.RECEIVER_ANDROID"/>
<category android:name="android.intrent.category.DEFAULT"/>
</intent-filter>
</receiver>
5.3.2.2.Java代码注册
MyReceiver receiver=new MyReceiver(); //创建自定义的MyReceiver类的一个对象
IntentFilter filter=new IntentFilter();
filter.addAction("com.tarena.exer05_3.RECEIVER_JAVA");
registerReceiver(receiver, filter);
说明
1、以上代码的第2、3行相当于<intent-filter>标签的功能,创建过滤器并设置过滤条件。
2、第4行注册广播接收者
5.3.3.广播与接收广播的实现步骤
步骤1、自定义一个广播接收者类,该类继承自BroadcastReceiver类,并重写onReceiver方法,在onReceiver方法中编写接收到广播需要处理的Java代码。示例代码如下:
public class ReceiverAndroid extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String hobby=intent.getStringExtra("hobby");
Log.i(Const.TAG_RECEIVER_ANDROID,hobby);
}
}
步骤2、在AndroidManifest.xml文件中配置一个<receiver/>标签,在<receiver/>标签中需要定义<intent-filter/>标签,该标签是指定接收器需要接收哪种广播,示例代码如下:
<receiver android:name=".ReceiverAndroid">
<intent-filter>
<action android:name="com.tarena.exer05_3.RECEIVER_ANDROID"/>
<category android:name="android.intrent.category.DEFAULT"/>
</intent-filter>
</receiver>
步骤3、在Activity、Service等组件中发送广播。发送的广播要通过设置Intent对象的action属性的一个字符串,该字符串用于与接收广播的BroadcastReceiver对象中声明的action相同,示例代码如下:
//以下发送广播,并传递hobby/I love Android键/值对数据
Intent intent=new Intent();
intent.putExtra("hobby", "I love Android");
intent.setAction("com.tarena.exer05_3.RECEIVER_ANDROID");
sendBroadcast(intent);
提示
步骤2和步骤3中红框所示的action属性的值相同,这样发送广播与接收广播者就在同一个“频段”上,将发送广播者比喻为电台在广播节目,那么广播接收者对象可以比喻为收音机,该收音机就能接收到广播以及该广播中存放的数据。
5.4.四大组件
5.4.1.概述
BroadcastReceiver是Android的四大组件之一,目前已学习了Activity、Service和BroadcaseReceiver三个组件,还有一个ContentProvider(内容提供者组件)将在后面介绍。
5.4.2.Android组件的特征
1、继承自Android预定义的类;
2、需要在项目清单中注册
3、通过Intent启动组件和进行数据传递。
5.5.权限
5.5.1.概述
Android系统规定:程序执行需要读取到安全敏感项时,必需在项目清单文件中申请相关权限请求。
示例代码如下:
<uses-permission
android:name="android.permission.CALL_PHONE">
</uses-permission>
<uses-permission
android:name="android.permission.INTERNET">
</uses-permission>
5.5.2.常用权限列表
1、android.permission.WRITE_SMS
允许程序写短信(Allows an application to write SMS messages)
2、android.permission.RECORD_AUDIO
允许程序录制音频(Allows an application to record audio)
3、android.permission.READ_SMS
允许程序读取短信息(Allows an application to read SMS messages.)
4、android.permission.CALL_PHONE
允许一个程序初始化一个电话拨号不需通过拨号用户界面需要用户确认(Allows an application to initiate a phone call without
5、android.permission.RECEIVE_BOOT_COMPLETED
允许一个程序接收到 ACTION_BOOT_COMPLETED广播在系统完成启动(Allows an application to receive the ACTION_BOOT
6、android.permission.READ_CONTACTS
允许程序读取用户联系人数据(Allows an application to read the user’s contacts data.)
7、android.permission.MOUNT_UNMOUNT_FILESYSTEMS
允许挂载和反挂载文件系统可移动存储(Allows mounting and unmounting file systems for removable storage. )
8、android.permission.INTERNAL_SYSTEM_WINDOW
允许打开窗口使用系统用户界面(Allows an application to open windows that are for use by parts of the system user interface. )
9、android.permission.INTERNET
允许程序打开网络套接字(Allows applications to open network sockets)
10、android.permission.CAMERA
请求访问使用照相设备(Required to be able to access the camera device. )
11、android.permission.CALL_PHONE
允许一个程序初始化一个电话拨号不需通过拨号用户界面需要用户确认(Allows an application to initiate a phone call without
5.5.3.申请权限的可视化操作步骤
除代码注册外,权限一般通过可视化步骤申请,既容易操作,也不会出错。以申请打电话权限为例:
步骤1、打开AndroidManifest.xml文件;
步骤2、按图-3提示操作,打开权限申请页面。
图-3
步骤3、按图-4提示操作,添加一个权限。
图-4
步骤4、按图-5提示操作:
图-5
步骤5、按图-6作,选择拨打电话的权限。
图-6
步骤6、保存项目。
5.6.Uri简介
5.6.1.概述
Uri(“统一资源标识符”三个英文单词的首字母):类似网络上的url地址,由以下三个部分构成:
(以百度网首页地址为例:http://www.baidu.com/index.html)
第一部分是协议,如http://
第二部分:主机名,如www.baidu.com
第三部分:资源索引值,通过索引值查找指定的资源。如index.html
提示:以上只是举例,Android为方便本地数据的读取也提供了一些协议和主机名,不一定都是以上所示的http协议。
5.6.2.常用Uri方法
Uri parse(String strUri):将字符串地址转换为一个Uri对象。
5.7.Intent的Data属性
5.7.1.概述
Data是Intent类的一个属性,用于向Action属性提供操作的数据。Data属性接收一个Uri对象。
5.7.2.示例代码
5.7.2.1.在模拟器中访问百度首页案例
步骤1、在项目清单文件中申请访问网络的权限,如图-5所示:
图-7
步骤2、在项目启动的Activity.onCreate()方法中输入如下代码:
//创建Uri对象,并将一个网址的字符串转换为Uri的数据
Uri uri=Uri.parse("http://www.baidu.com");
intent=new Intent();
intent.setData(uri);//设置intent.data属性值为一个地址值
// Intent.ACTION_VIEW 常量值的作用参见图-1
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);//启动Android系统预定义的访问网络的Activity
5.7.2.2.拨打电话案例
步骤1、在项目清单文件中申请拨打电话的权限,如图-7中红框之上的权限。
步骤2、在项目启动的Activity.onCreate()方法中输入如下代码:
//以下五行代码启动系统预设的拨打电话的Activity
intent=new Intent();
intent.setAction(Intent.ACTION_CALL);
Uri uri=Uri.parse("tel:5554");//将代表电话号码的字符串转换为uri
intent.setData(uri);//将uri存放在intent.data属性中
startActivity(intent);//启动Android系统预定义的打电话窗口
5.8.显示与隐式Intent
5.8.1概述
Intent中文:意图 ,在组件间跳转时,Android系统提供了隐式意图和显示意图两种方式。
1、显示意图方式:通过setClass()方法指明要跳转的组件的类名.class。
2、隐式意图方式:通过设置Intent的Action和Categroy属性值,跳转至符合这两个属性值的组件。本章以上所举的示例均为隐式意图。
5.8.2.两种意图方式的比较
1、显示意图的性能较高,因直接指定目标组件,无需系统遍历比较。但只能局限在当前的应用程序中,无法启动其它进程中的组件。
2、隐式意图的性能不如显示意图,但使用范围广,不但能启动当前应用程序内的组件,还能启动其它应用程序的进程。
若在当前应用程序内,建议使用显示意图方式启动组件。
5.9.单击事件的高效设计
5.9.1.概述
前面学习了用内部匿名类实现控件的单击事件,以下介绍另一种响应单击事件的实现方式,该方式因无需创建对象,所以执行效率要高于内部匿名类的设计方式,而且在编写多个同类控件的单击事件时,能使代码更清晰易读。
5.9.2.原理及实现步骤
OnClickListener事件是用于处理控件的单击事件,该接口中定义了一个onClick()方法,通过实现该方法,达到响应用户单击控件的目的。
前面在编写响应控件的单击事件时,通过new OnClickListener()的方式,以内部匿名类直接创建实现该接口的一个对象。Android也允许用以下方式实现控件的单击事件方式:
public class MainAct extends Activity implements OnClickListener{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//步骤1-以下创建了三个按钮对象
Button btnJava=(Button)findViewById(R.id.btnJava);
Button btnAndroid=(Button)findViewById(R.id.btnAndroid);
Button btnUnregister=(Button)findViewById(R.id.btnUnregister);
//步骤2-以下注册三个按钮的单击事件
btnJava.setOnClickListener(this);
btnAndroid.setOnClickListener(this);
btnUnregister.setOnClickListener(this);
}
//步骤3-实现OnClickListener接口的OnClick()方法
@Override
public void onClick(View v) {
Intent mintent;
switch(v.getId()){
case R.id.btnAndroid:
//编写相应的代码
break;
case R.id.btnJava:
//编写相应的代码
break;
case R.id.btnUnregister:
//编写相应的代码
break;
}
}
5.10.常见错误
5.10.1.按钮事件不响应
采用5.9.设计控件的单击事件时,若出现按钮单击事件未响应的现象。通常是忘记了步骤2注册控件的单击事件。
5.10.2.用隐式意图启动组件不成功
解决方法:
1、启动的Action和注册的Action的字符串不相同,最好采取复制的方式,保证两个字符串相同。
2、<category>标签没有写,或该属性值书写错误。一般情况下用系统默认的值,如下所示:
<category android:name="android.intent.category.DEFAULT"/>
5.10.3.启动Activity出现异常
通过观察日志窗口,发现错误信息:
ERROR/AndroidRuntime(500): android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.tarena.exer05_1.SECOND_ACTIVITY }
原因:
1、Activity在清单文件中没有配置
2、隐式意图中的Action没有对应的Activity存在
3、标签中含有大写字母
5.11.常用技巧
5.11.3.类成员变量命名规范
类成员变量前加小写字母m(member:成员)。
5.11.4.快速查找带源代码的Activity
以ApiDemos为例,昨天已讲解了如何创建ApiDemos,现在按以下步骤操作:
步骤1、运行该项目,如图-8所示:
图-8
步骤2、按图-8操作,出现图-9
图-9
步骤3、按图-9操作,出现图-10
图-10
步骤4、切换到日志窗口,出现如图-11所示:
图-11
步骤5、通过查看图-11中红框中的信息,发现一个名为DialogActivity的Activity被启动了。现在(在确保关闭中文输入法的前提下)按Ctrl+Shift+R组合键(快速搜索类、XML文件),出现图-12:
图-12
步骤6、按图-12操作,将打开DailogActivity.java源代码。
5.11.5.用重构方式实现松耦合设计
对于项目中的类中的字符串、整数的数据,通常定义为常量,用有意义的常量名表示数据,能有效提高代码的可读性,利于代码的维护。
项目通常有许多类,将这些类中的常量提取出来,统一放在一个工具类中,这样做的好处是修改方便,实现松耦合。这称为重构。
示例:图-13所示的项目exer05_3中有四个类:
图-13
其中的Const.java(Const:英文“常量”单词的头四个字母)存放了另外三个类中用到的字符串常量,以下是Const.java的代码:
package com.tarena.exer05_3;
//工具类,将项目中常用的字符串定义为常量
public class Const {
public static final String TAG_MAIN="MainAct";
public static final String TAG_RECEIVER_JAVA="ReceiverJava";
public static final String TAG_RECEIVER_ANDROID="ReceiverAndroid";
}
这样,以后若因业务需求,需要修改这些常量值,只要在Const类中进行即可。同时,其它类引用这三个常量都只与Const关联,它们之间不互相关联,也降低了各类之间的耦合性。
5.11.6.在BroadcastReceiver中不要直接启动Activity
从用户使用体验的角度考虑,在接收到系统发送的广播时,以下情况可以启动Activity,显示窗口,如接电话、闹钟定时提醒。但其它情况下应尽量避免弹出窗口,影响用户操作。对于接收到的广播应该以通知的方式提醒用户。
提示:通知是Android的一项技术,将在后面学习到。
5.11.7.为什么注销广播?
注册的广播接收者对象,在确定不再使用时应该注销,减少对系统的资源的占用。
5.11.8.注册与注销BroadcastReceiver的时机
应该在onStart()方法中注册BroadcastRecevier,在onStop()方法中注销。