关键技能和概念
l 使用Intents
l 创建与手机硬件交互的代码
l 了解dialing与calling的区别
至此,本章已经为你介绍了Android编程基础知识。你已经研究了Android 应用的概要,并在你Android服务端安装了你的首批应用。你不仅了解了如何使用Views和setContentView(),还了解了如何在XML创建UI。这些技能帮你创建了一个静态的应用。现在你还没做的是使用应用接口与平台创建的目标硬件交互——手机。
你不会看不到的事实是,从本质上讲,Android创建的目标平台就是一个手机。Android运行的设备底层硬件,是基于p2p通信的目而设计的。如果去除Android SDK添加大手机上的所有花哨的功能,它仍必须能够接打电话。由于这个原因,本章就关注和硬件交互的代码。
本章结束时,你应该拥有了与手机的一些基础功能交互的技能。你会使用拨号器接打电话。这些工具与技能会成为你在这可扩展的平台创建有用的应用的关键点。
你阅读本书,因为你想设计能在手机上运行的应用,所以你应该了解Android如何与手机硬件进行交互——尤其是用手机接打电话的流程。
当我们想到手机,就会浮现一些基本功能。第一个也是最显眼的就是接待电话。这毋庸置疑是一个手机的典型功能。有一些附带的特性使手机更易使用,如保存和管理通讯录功能,存储和浏览未接电话的功能。如果你阅读本章,你会访问和操作所有这些功能的代码。
本书中你会看到的第一个手机功能就是打的话。你会使用Intent创建一个应用,控制手机拨号器进行拨号。然后,你会给这个应用添加一些花哨的功能。
在你跟手机拨号器交互之前,你需要了解完成这项工作所使用的代码类型。Android在应用中使用Intents作特殊的工作。一旦你掌握了Intents的用法,就会为你打开一个崭新的应用开发的世界。本节就定义什么是Intent和它的用法。
Intent是Android的方法,用来将部分信息从一个Activity传递给另一个。一个Intent简单的说,向Android传递你的意图。简单的说,你可以将一个Intents看做是Activity之间传递的信息。例如假设你有一个打开浏览器,在你的Android设备上展示页面的Activity。你的Activity肯定会给Android Intent Resolver发送一个“intent to open x page in the web browser”的WEB_SEARCH_ACTION的Intent。Intent Resolver解析一串Activity列表,并选择最适合你的Intent;本例中是Web Browser Activity。Intent Resolver然后就将你的页面传递到web浏览器上,并启动Web Browser Activity。
Intent分为两类:
l Activity Action Intents 用来在应用之外调用Activity的Intents。只有一种Activity能够处理Intent。如,对于web浏览器来说,你需要打开Web Browser Activity来显示页面一样。
l Broadcast Intents 用来发送复杂Activity的Intents。Broadcast Intent的一个例子可能是Android发送的当前电池容量的信息。任何Activity都能处理这种Intent并采取相应对策——例如,如果电池容量低于一定指标就取消一个Activity。
表7-1列出并描述了当前可用的Activity Action Intents。如你所视,大多数情况下,Intent的名字就描述了它的功能。
表7-1 Activity Action Intents
表7-2列出并描述了当前可用的Broadcast Intent。当你需要创建一个具体Intent接收器时参考该列表。
表7-2 Broadcast Intent
Intent仅仅是全部的三分之一。一个Intent实际上仅仅是做什么的意图;一个Intent实际上本书什么都不做。你需要Intent过滤器和Intent接收器来监听,中断Intent。
一个Intent接收器,如同Activity的信箱。Intents接收器用来让Activity接收指定的Intent。拿前面的web浏览器例子来说,Web Browser Activity用来接收web 浏览器的Intents。这样的系统允许无关的Activity忽略他们不需要处理的Intents。也允许Activity在其它Activity的帮助下利用Activity,而不需要知道如何调用它。
利用Intents和Intent 接收器,一个Activity可以发送Intent,而另一个可以接受它。然而,需要什么来关系两个Activity之间发送的信息类型。这就是Intent过滤器的由来。
Intent过滤器是Activity用来描述它们接收的Intent类型。更重要的是,他们概述了应该随着Intent传递的数据类型。因此,在我们的例子中,我们想让web浏览器打开一个web页面。Intent过滤器会声明,随着WEB_SEARCH_ACTION传递的数据需要符合URL的格式。
下一节,你会开始利用Intent打开和使用手机拨号器。
由于你明白了什么是Intent,那么时候实践一个例子了。本节为你展示如何使用DAIL_ACTION Intent打开手机拨号器。你会用Intent传递一个电话号码。如果你的应用正常工作,你会在拨号器中看到你用Intent传递的号码。
第一步是为Activity创建一个新项目(见第五章说明)。将项目命名为AndroidPhoneDialer。下面的插图显示的该项目的Android项目向导。
在Eclipse中打开新的应用,首先从main.xml中删除包含Hello World声明的TextView。删除TextView后,main.xml文件应该看起来像这样:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
</LinearLayout>
要使用DIAL_ACTION Intent你需要在你的项目中添加两个新包,如下。第一个包允许你安装Intents,第二个允许你解析URLs。
import android.content.Intent;
import android.net.Uri;
下一步就是创建你的Intent,创建Intent的语法如下:
Intent <intent_name> = new Intent(<Android_Intent>,<data>)
对于你的应用,用DialIntent替换第一个参数。要得到第二个参数的值,参考表7-1中的Activity Action列表。你会发现,要调用拨号器,你需要使用DAIL_ACTION Intent。要正确的调用Intent,使用Intent. DAIL_ACTION的格式。最后一个参数,<data>,就是手机号。DAIL_ACTION Intent引入URL数据。因此你需要使用Uri.Parse来解析你的手机号码。使用Uri.Parse能确保DIAL_ACTION Intent能理解你视图拨的号码。你给Uri.parse传递一个字符串,代表你要拨打的电话号码,本例是“tel:5551212”.
创建Intent的最后调用应该像这样:
Intent DialIntent = new Intent(Intent.DIAL_ACTION,Uri.parse("tel:5551212"));
创建Intent后,你要告诉Android你想在新的Activity中启动拨号器。你应该给setLaunchFlags传递合适的参数。下面就是你可以设置的可能启动标识列表
● NO_HISTORY_LAUNCH 启动Activity而不记录在系统的启动记录中
● SINGLE_TOP_LAUNCH 通知系统不启动正在运行的Activity
● NEW_TASK_LAUNCH 启动Activity
● MULTIPLE_TASK_LAUNCH 启动Activity,即使它已经运行
● FORWARD_RESULT_LAUNCH 允许新Activity接收传递给当前Activity的结果
本例中,你要使用Intent.NEW_TASK_LAUNCH,仅仅让你打开一个拨号器Activity的示例。
DialIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH );
创建拨号Intent的最后一步就是启动Activity。(更精确地讲,你要告诉Android你想启动拨号器的新任务。最终要Android启动Dialer Activity。)要告诉Android你想启动拨号器,你需要使用startActivity():
startActivity(DialIntent);
注意你给startActivity()传递你的Intent。Intent然后就被传到Android,动作也就解决了。AndroidPhoneDialer.java的全代码应该看起来这样:
package android_programmers_guide.AndroidPhoneDialer;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.net.Uri;
public class AndroidPhoneDialer extends Activity {
/** Called when the Activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
/** Create our Intent to call the Dialer */
/** Pass the Dialer the number 5551212 */
Intent DialIntent = new
Intent(Intent.DIAL_ACTION,Uri.parse("tel:5551212"));
/** Use NEW_TASK_LAUNCH to launch the Dialer Activity */
DialIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH );
/** Finally start the Activity */
startActivity(DialIntent);
}
}
现在你可以编译AndroidPhoneDialer并在模拟器上运行。编译和运行应用的流程在前面的章节中已经讲了吗,所以你应该很熟悉。一旦你运行你的应用,模拟器就会启动。在漫长的启动过程后,你的Activity就会启动。
如果你按例子中的代码来,你会看到下面的结果:
如你看到的,你已经打开的电话的Dialer Activity。拨号器现在了你在里面输入的号码,5551212。使用模拟器,点击Send按钮,电话就会拨打555-1212——当然是虚拟的。
如果你想创建允许用户在拨打之前编辑号码或者确认他们确实要拨号的应用,刚展示的Dialer Activity才有用。如果你想创建一个真实的拨号应用,你应该做什么?答案就在下一节。
本节汇总,你会了解当拨号时,Intent为你的Activity添加了什么。你也会学会在Activity代码中于何处添加你选择的Intent。甚至,你还会学会如何将拨打的电话号码解析为一个URI。
要从Dialer Activity转到Call Action,你需要对你的代码做一些小的变动。本节中,为了在打开拨号器后拨通一个电话,你会编辑你的AndroidPhoneDialer Activity。
为你的Activity添加Intent
你还需要Intent和Uri包——如下——将它们放在AndroidPhoneDialer.java文件的首部:
import android.content.Intent;
import android.net.Uri;
这些包可以使你不仅实例化所需的Intent,还将所需的电话号码数据传递给Intent(利用Uri包)。
在表格7-1中浏览一下Activity Action Intent的列表,如本章前部分所示。和名字一样,在Activity中用到的Intent就是CALL_INTENT。和DAIL_ACTION打开Android拨号器同理,CALL_ACTION会启动手机的拨号程序,并为提供的号码初始化拨号。
要创建Intent,使用的流程和你在拨号中使用的相同,不过这次是CALL_ACTION。
Intent CallIntent = new
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212"));
注意用Uri.prase为Activity传递一个正确的电话号码。下一步是告诉Android你想对Activity进行启动设置,然后就启动它。这通过下面两行代码实现。
CallIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH );
startActivity(CallIntent);
在第一个行,你发送启动标识NEW_TASK_LAUNCH。这会启动一个Call Activity实例。最后你告诉Android使用Intent启动Activity。完毕时,你的AndroidPhoneDialer.java文件看起来像这样:
package android_programmers_guide.AndroidPhoneDialer;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.net.Uri;
public class AndroidPhoneDialer extends Activity {
/** Called when the Activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
/** Create our Intent to call the device's Call Activity */
/** Pass the Call the number 5551212 */
Intent CallIntent = new
Intent(Intent.CALL_ACTION,Uri.parse("tel:5551212"));
/** Use NEW_TASK_LAUNCH to launch the Call Activity */
CallIntent.setLaunchFlags(Intent.NEW_TASK_LAUNCH );
/** Finally start the Activity */
startActivity(CallIntent);
}
}
现在编译应用并观察结果:你会在插图中看到相似的错误。
我特意让你看到这个错误,是因为它显示了我们没有研究过的Android的另一面。下面就是错误信息:
Application_Error:
…
Java.lang.SecurityException:
Permission Denial: starting Intent
…
Android通过分配给他们的权限来管理部分Action,如下所述。
编辑Action权限
大部分Activity Action Intent归为一类,即在Android允许响应之前必须拥有适当的权限。如同大多数系统,Android仅仅需要确保那那些拥有正确凭据的Activity允许执行拥有外部Activity的动作。这就是可用的权限:
● ACCESS_ASSISTED_GPS
● INTERNAL_SYSTEM_WINDOW
● ACCESS_CELL_ID
● RAISED_THREAD_PRIORITY
● ACCESS_GPS
● READ_CONTACTS
● ACCESS_LOCATION
● READ_FRAME_BUFFER
● ACCESS_SURFACE_FLINGER
● RECEIVE_BOOT_COMPLETED
● ADD_SYSTEM_SERVICE
● RECEIVE_SMS
● BROADCAST_PACKAGE_REMOVED
● RECEIVE_WAP_PUSH
● BROADCAST_STICKY
● RUN_INSTRUMENTATION
● CALL_PHONE
● SET_ACTIVITY_WATCHER
● CHANGE_COMPONENT_ENABLED_STATE
● SET_PREFERRED_APPLICATIONS
● DELETE_PACKAGES
● SIGNAL_PERSISTENT_PROCESSES
● DUMP
● SYSTEM_ALERT_WINDOW
● FOTA_UPDATE
● WRITE_CONTACTS
● GET_TASKS
● WRITE_SETTINGS
● INSTALL_PACKAGES
比较这个权限列表和表7-1中的Intent列表。你会发现大部分Intent都需要匹配一个通信权限。CALL_ACTION Intent也不例外。你需要为你的Activity分类一个CALL_PHONE权限来执行你的Intent。