gdata-src.java-1.47.0
下载地址:http://download.csdn.net/detail/dingbuoyi/4272908
gdata-samples.java-1.47.0
下载地址:http://download.csdn.net/detail/dingbuoyi/4272911
google-api-java-client-1.8.0-beta
下载地址:http://download.csdn.net/detail/dingbuoyi/4272915
google-oauth-java-client-1.8.0-beta
下载地址:http://download.csdn.net/detail/dingbuoyi/4272917
在发布Android应用的时候,很容易出现问题,下面这个问题比较常见。
1. 异常log:
Proguard returned with error code 1.See console
[2011-08-0802:32:46-CoolProject] proguard.ParseException:Unknown option 'Projects\Eclipse'in argument number 9
[2011-08-0802:32:46-CoolProject]
at
proguard.ConfigurationParser.parse(ConfigurationParser.java:172)[2011-08-0802:32:46-CoolProject]
at proguard.ProGuard.main(ProGuard.java:484)
2. root cause:
存放project的目录名称中存在空格
3. 解决方法:
去掉project的目录名称中存在的空格,注意:尽量使用英文且不留空格
第4 章 Intent 和Broadcast 面对面
在第 3 章介绍了 Android 应用的核心组件: Activity, Service 和 Broadcast receiver ,这些组件通过消息的机制联系起来,承载这个消息的组件是 Intent , Broadcast 也可以完成这个通信的任务。不过 Broadcast 只能发给合适的 Broadcast Receiver 。他们最大的特色是可以解耦,使各功能模块独立。
本章我们将从基本理论介绍开始,深入分析个中原理,再以例子的形式详解 Intent 和 Broadcast 的运用。主要内容框架如下:
4.1 Android 应用程序的核心 ——Intent
4.2 Android Intent 解析
4.3 利用 Intent 来广播 (Broadcast) 事件
4.4 应用实例详解
4.1 Android 应用程序的核心 ——Intent
Intent 是一种运行时绑定( runtime binding )机制,它能在程序运行的过程中连接两个不同的组件。 Intent 包含一个执行操作的抽象数据结构,这个数据结构用于传递给执行操作的组件,或者,常见于 broadcast 的情况,用于描述正在执行或者已经发生的事情。对于第 3 章中介绍的三种组件,有独立的传送 intent 的机制 :
· Activity : 创建新的活动或者让一个现有的 Activity 执行新的任务,可以通过调用传递 intent 到 Context.startActivity() 或 Activity.startActivityForRestult() 方法来达成。
· Service : 启动一个新的服务,或者向一个已有的服务传递新的指令,可以调用 Context.startService() 或 Context.bindService() 方法,此方法的将上下文对象 context 与 Service 绑定。
· Broadcast Receiver : 通过 Context.sendBroadcast() 、 Context.sendOrderBroadcast() 和 Context.send-StickBroadcast() 这三个方法可以发送 BroadcastIntent 。 BroadcastIntent 发送后,所有已注册的拥有与之相匹配 IntentFilter 的 BroadcastReceiver 就会被激活。这种机制被广泛运用于设备或系统状态变化的通知,一个常见的例子是,当 Android 的电池电量过低时,系统会发送 Action 为 BATTERY_LOW 的广播,接着任何可匹配该 Action 的 IntentFilter 注册的 BroadcastReceiver 都会各自运行自定义的处理代码,比如关闭设备的 WIFI 和 GPS 以节省电池消耗。
在每种情况下, Android 系统查找合适的 activity 、 service 、 broadcast receivers 来响应意图,如果有必要的话,初始化他们。这些消息系统之间没有重叠,即广播意图仅会传递给广播接收者,而不会传递活动或服务,反之亦然。
4.1.1 Intent 的组成
Android 中提供了 Intent 机制来协助应用间的交互与通讯, Intent 负责对应用中一次操作的动作、动作涉及数据、附 加数据进行描述, Android 则根据此 Intent 的描述,负责找到对应 的组件,将 Intent 传递给调用的组件,并完成组件的调用。 Intent 不仅可用于 应用程序之间,也可用于应用程序内部的 Activity/Service 之间的交互。因此, Intent 在这里起着一个媒体中介的作 用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。
简而言之, Intent 就是组合一定的信息,然后传达这些信息给特定的对象,或者广播一些信息某些对象;这里涉及两方面的内容:
A) 消息的发送;
B) 消息的接收;
完成这些神奇的功能, Intent 需要什么样的结构,由哪些零件组成呢?
Intent 这个神奇的组件很抽象,是一个抽象的信息捆绑结构,在这个结构中发挥作用的基本零件可以分为组件名称 (Component Name) 、动作( Action )、数据( Data )、类型( Category )、附加信息( Extra )和标识位( Flag ) 6 部分,下面是详细介绍。
4.1.1.1 Intent 的组件名称( Component Name)
组件名称是 Intent 目标组件的名称。组件名称是一个 ComponentName 对象,这种对象名称是目标组件类名和目标组件所在应用程序的包名的组合。 Android 系统匹配目标组件有两种方式:
A. 显示 Intent :对于明确指出了目标组件名称的 Intent (调用 setComponent() 方法或 setClass() 方法来指定)。
B. 隐式 Intent :对于没有明确指出目标组件名称的 Intent 。 隐式 Intent ,由于没有明确的目标组件名称,所以必须包含足够的属性信息,他们是: Action , Data , Category 。再 由 Android 系统帮助应用程序寻找与 Intent 请求意图最匹配的组件。
所以,设置 Intent 的 Component 定义了 Android 系统匹配目标组件的显示方法。
4.1.1.2 Intent 的动作( Action )
Action 描述 Intent 所触发动作名字的字符串,对于 BroadcastIntent 来说, Action 指被广播出去的动作。理论上 Action 可 以为任何字符串,而与 Android 系统应用有关的 Action 字符串以静态字符串常量的形式定义在了 Intent 类中。表 4-1 列出了当前 Android 系统中常见的部分 Activity Action Intent 的 Action ,以供参考。
图 4-1 Android 常用部分 Action 表
4.1.1.3 Intent 的数据( Data )
Data, 就是 Action 所要操作的数据。 Android 中采用指向数据的一个 URI 来表示,如在联系人应用中,一个指向某联 系人的 URI 可能为: content://contacts /1 。对于不同的动作,其 URI 数据的类型是不同的(可以设置 type 属性指 定特定类型数据),如 ACTION_EDIT 指定 Data 为文件 URI ,打电话为 tel:URI ,访问网络为 http:URI ,而由 content provider 提供的数据则为 content: URIs 。
正确设置 Intent 的数据对于 Android 寻找系统中匹配 Intent 请求的组件很重要。设置的精确可以提高效率,设置错了,得出的数据牛头不对马嘴,甚至程序会崩溃。
4.1.1.4 Intent 的类型( Category )
Category 是对被请求组件的额外描述信息 。 Android 也在 Intent 类中定义了一组静态字符串常量表示 Intent 不同的类别,表 4-2 列出了 Android 系统中常见的部分 Category 常量。
图 4-2 Android 系统中常见的部分 Category 常量
4.1.1.5 Intent 的附加信息( Extra )
Extra 当我们使用 Intent 连接不同的组件时,有时需要在 Intent 中附加额外的信息,以便将数据传递给目标 Activity 。比如 ACTION_TIMEZONE_CHANGED 需要带有附加信息表示新的时区。
Extra 用键值对结构保存在 Intent 对象当中, Intent 对象通过调用方法 putExtras() 和 getExtras() 来存储和获取 Extra 。 Extra 是以 Bundle 对象的形式来保存的, Bundle 对象提供了一系列 put 和 get 方法来设置、提取相应键值信息。在 Intent 类中同样为 Android 系统应用的一些 Exrta 的键值定义了静态的字符串常量。如图 4-3 。
图 4-3 Extra 部分常量
4.1.1.6 Intent 的标识位( Flag )
Intent 的标识位( Flag )是 Android 的过滤的核心组件,决定了启动的目标组件的方式。即告诉 Android 系统如何启动一个 Activity 及如何执行 Actvity 的任务。在 Intent 类中同样为 Android 系统应用的一些 Flag 的键值定义了静态的字符串常量。如图 4-4 。
图 4-4 Flag 部分常量表
4.2 Android Intent 解析前面提到, Android 使用 Intent 有显式和隐式两种方式:
显示的方式,是定义 Intent 对象,然后通过调用 setComponent 接口设置目标组件的 Component name, 如下代码所示:
Intent intent = new Intent();
Intent.setComponent(this.getComponentName());
隐式的方式,就是在代码中没有明确声明启动哪个组件,而是通过 IntentFilter 过滤筛选出目标组件。那么 IntentFilter 是个啥东西呢? IntentFilter 是描述组件所能响应的 Intent 的一个属性组合,具体就是说明组件希望接收什么类型的请求,处理什么类型的数据,以什么方式处理等。 应用程序的组件为了告诉Android 自己能响应、处理哪些隐式Intent 请求,可以声明一个甚至多个IntentFilter 。
IntentFilter 在应用开发的过程中,主要是在 Android-Manifest.xml 声明,具体形式如下:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="oauth" />
</intent-filter>
隐式 Intent 和 IntentFilter 进行比较时的三要素: Action 、 Data 、 Category 。
一个隐式 Intent 请求要能够传递给目标组件,必需通过以上三个方面的检查。如果任何一方面不匹配, Android 都不会将该隐式 Intent 传递给目标组件。
Android 解析 Intent 的详细过程如下图 4-5 :
图 4-5 Android 解析 Intent 的详细过程
Note: 在通过和 IntentFilter 比较来解析隐式 Intent 请求时, Android 将以下三个因素作为选择的参考标准。 Action , Data , Category 。而 Extra 和 Flag 在解析收到 Intent 时是并不起作用的。
v Action 解析
<intent-filter> 元素中可以包括子元素 <action> ,一条 <intent-filter> 元素至少应该包含一个 <action> ,否则任何 Intent 请求都不能和该 <intent-filter> 匹配。
如果 Intent 请求的 Action 和 <intent-filter> 中个某一条 <action> 匹配,那么该 Intent 就通
过了这条 <intent-filter> 的动作测试。
如果 Intent 请求或 <intent-filter> 中没有说明具体的 Action 类型,那么会出现下面两种情况。
v Data 解析
数据在 <intent-filter> 中的描述如下:
<data> 元素指定了希望接受的 Intent 请求的数据 URI 和数据类型, URI 被分成三部分来进行匹配: scheme 、 authority 和 path 。其中,用 setData() 设定的 Intent 请求的 URI 数据类型和 scheme 必须与 IntentFilter 中所指定的一致。若 IntentFilter 中还指定了 authority 或 path ,它们也需要相匹配才会通过测试。
v Category 解析
<intent-filter> 元素可以包含 <category> 子元素,比如:只有当 Intent 请求中所有的 Category 与组件中某一个 IntentFilter 的 <category> 完全匹配时,才会让该 Intent 请求通过测试, IntentFilter 中多余的 <category> 声明并不会导致匹配失败。一个没有指定任何类别测试的 IntentFilter 仅仅只会匹配没有设置类别的 Intent 请求。
Note: 显式 Intent 直接用组件的名称定义目标组件,这种方式很直接。显式 Intent 多用于在应用程序内部传递消息。比如在某应用程序内,一个 Activity 启动一个 Service 。隐式 Intent 恰恰相反,由于开发人员往往并不清楚别的应用程序的组件名称,它不会用组件名称定义需要激活的目标组件,它更广泛地用于在不同应用程序之间传递消息。
4.3 利用 Intent 来广播 (Broadcast) 事件
在 Android 系统中,广播( Broadcast )是在组件之间传播数据( Intent )的一种消息机制;这些组件甚至是可以位于不同的进程中,这样它就像 Binder 机制一样,起到进程间通信的作用。
在 Android 系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,如果两个组件位于不同的进程当中,那么可以用 Binder 机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。然而,广播机制却是不可替代的,它和 Binder 机制不一样的地方在于,广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
广播在 Android 系统中无处不在,为了方便应用程序处理广播, Android 提供了很多 Action, 应用程序可以直接接收甚至发出这些 Action 。下图 4-6 是部分常量:
图 4-6 Broadcast 系统部分常量
4.3.1 Broadcast 的广播流程
Broadcast 的广播在 Android 系统中有一个标准的流程。
首先,定义自己的广播接收器,这里需要设置广播的 Action , Category 等信息。
其次,注册 broadcast, 这里有两种注册方式,静态和动态注册。这两种注册方式的区别是,静态注册是常驻型广播,它的生命周期跟宿主 Activity 的生命周期脱离,即程序关闭之后,如果有信息广播来,程序也会被系统调用自动运行;动态注册不是常驻型广播,它的生命周期跟宿主 Activity 的生命周期一致,即程序关闭后,广播的生命周期也结束了,即使有广播信息来,系统也不会自动调用程序。具体实现方式后面讨论。
其三,在需要发送信息的地方,把要发送的信息和用于过滤的信息(如 Action 、 Category )装入一个 Intent 对象,然后通过调用 Context.sendBroadcast() 、 Context.sendOrderBroadcast() 或 Context.sendstickyBroadcast() 方法,把 Intent 对象以广播的方式发出去。
其四,筛选 Broadcast receiver 。这个过程类似于 IntentFilter 的过程,完全由 Android framework 层完成。通过 Action 等属性,合适的 Broadcast 接收器会被筛选出来。
最后, BroadcastReceiver 的 void onReceive(Context curContext, Intent broadcastMsg) 方法会被调用。处理消息的内容,执行相应的操作。这个方法返回后 BroadcastReceiver 就停止了。
具体的过程,可以参考图 4-7 广播流程图
图 4-7 广播流程图
4.3.2 Broadcast 的实现过程
这里介绍 Broadcast 的实现过程的框架,以理清 Broadcast 的使用逻辑。
首先,定义一个广播接收器,继承自 BroadcastReceiver, 并明确需要使用的 Broadcast action ,如果需要处理系统的广播,则直接通过 ” android.intent.action.XXX ” 引用就可以了,如果自定义广播,则需要定义 Action 。如下代码所示:
public class BRDemo extends BroadcastReceiver {
public static String YOUR_CUSTOM_BROADCAST_ACTION_NAME = “ANY_NAME_HERE”;
/* 必须重载 onReceive 方法 */
@Override
public void onReceive(Context context, Intent intent) {
//todo: 在这里添加处理的代码
…………..
}
}
其次,注册 Broadcast Receiver 。前面提到过, Android 提供了两种注册广播接受者的形式,分别是动态注册和在 xml 中静态注册。他们之间的区别就是作用的范围不同,程序动态注册的接收者只在程序运行过程中有效,而在 xml 注册的接收者不管你的程序有没有启动有会起作用。
A. 动态注册方式。
动态注册的方式。
// 注册广播
registerReceiver(instance, intentFilter);
//Note: 在 onDestroy 要注销广播
调用 unregisterReceiver(..) 函数 ;
B. 静态注册方式。
<?xml
version="1.0" encoding="utf-8"? <?xml
version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android
"
package="YOUR_PACKAGE_NAME"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity
android:name="YOUR_ACTIVITY "
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="BroadcastRecTest">
<intent-filter>
//
定义
Receiver
的
Action
<action
android:name=" YOUR_CUSTOM_BROADCAST_ACTION_NAME" />
</intent-filter>
</receiver>
</application>
</manifest>
最后,可以发送广播了。
// 生成广播处理
YOUR_BROADCAST_RECEIVER_CLASS instance= new YOUR_BROADCAST_RECEIVER_CLASS();
// 实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter(YOUR_CUSTOM_BROADCAST_ACTION_NAME);
4.4 应用实例详解以上我们详细讨论 Android 的 intent 和 broadcast ,也说明了它们的使用方式,接下来我们将通过一个实例,讲解 intent 和 broadcast 的使用。
1. 新建一个 Android 项目,这个项目使用 Intent 发送一个自定义 Broadcast 。如图 4-8 :
图 4-8 Android demo
2. 定义 Broadcast 的 Action name 。如图 4-9 :
图 4-9 定义广播 Action
3. 新建一个 class, 继承 BroadcastReceiver, 这个方式实现,是处理静态的广播如图 4-10 ,处理动态广播的 class 如图 4-12 :
图 4-10 新建广播接收器
4. 注册 BroadcastReceiver, 静态注册如图 4-11 ,动态注册如图 4-12 :
图 4-11 静态注册
图 4-12 动态注册
5. 发送广播。如图 4-13 :
图 4-13 发送广播
以上基本完成了利用 Intent 发送广播的例子,其中涵盖了静态和动态注册两种方式,源代码可以参考 /demo/source/demo_044 。
在 Android 上我们可以完成很多神奇的应用,下面列举常用的一些例子:
显示网页:
Uri uri =
Uri.parse(URL_YOU_WANT_TO_BROWSE);
Intent it = new
Intent(Intent.ACTION_VIEW,uri);
startActivity(it);
显示
google
地图
:
Uri uri =
Uri.parse("geo:XXX,XXX");
Intent it = new Intent(Intent.Action_VIEW,uri);
startActivity(it);
Maps
路径规划
:
Uri uri=
Uri.parse("YOUR_URL_HERE");
Intent it = new Intent(Intent.ACTION_VIEW,URI);
startActivity(it);
拨打电话
:
Uri uri =
Uri.parse("tel:xxxxxx");
Intent it = new Intent(Intent.ACTION_DIAL,
uri);
startActivity(it);
Note:
<uses-permission id="Android.permission.CALL_PHONE" />
发送
SMS/MMS:
Intent it =
new Intent(Intent.ACTION_VIEW);
it.putExtra("sms_body",
"android
开发网欢迎您
");
it.setType("vnd.android-dir/mms-sms");
startActivity(it);
发送短信
:
Uri uri =
Uri.parse("smsto:10086");
Intent it = new
Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body",
"10086"); //
正文为
10086
startActivity(it);
发送彩信
:
Uri uri = Uri.parse("YOUR_CONTENT_PATH");
//
该
Uri
根据实际情况修改,
external
代表外部存储即
sdcard
Intent it = new
Intent(Intent.ACTION_SEND);
it.putExtra("sms_body",
"android123.com.cn");
it.putExtra(Intent.EXTRA_STREAM,
uri);
it.setType("image/png");
startActivity(it);
发送
Email:
Uri uri = Uri.parse("mailto:MAIL_ADDRESS");//URI
指定邮箱地址实现
Intent it = new
Intent(Intent.ACTION_SENDTO, uri);
startActivity(it);
Intent it = new Intent(Intent.ACTION_SEND);//
通过
Intent
实现
it.putExtra(Intent.EXTRA_EMAIL,
"YOUR_MAIL_ADDRESS");
it.putExtra(Intent.EXTRA_TEXT,
"MESSAGE_BODY");
it.setType("text/plain");
startActivity(Intent.createChooser(it,
"
选择一个邮件发送客户端
"));
Intent it=new Intent(Intent.ACTION_SEND);
);//
通过
Intent
实现,数据放在外部,添加抄送
String[] tos={"SUBJECT_MAIL_ADDRESS"};
//
发送到
String[] ccs={"CC_MAIL_ADDRESS"};
//
抄送
it.putExtra(Intent.EXTRA_EMAIL,
tos);
it.putExtra(Intent.EXTRA_CC,
ccs);
it.putExtra(Intent.EXTRA_TEXT,
"
正文
");
it.putExtra(Intent.EXTRA_SUBJECT,
"
标题
");
it.setType("message/rfc822");
//
编码类型
startActivity(Intent.createChooser(it,
"
选择一个邮件客户端
"));
Email
添加附件:
Intent it =
new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_SUBJECT,
"
正文
");
it.putExtra(Intent.EXTRA_STREAM,
"ATTACHMENT_FILE_PATH"); //
附件的路径
sendIntent.setType("RESOURCE_TYPE");
startActivity(Intent.createChooser(it,
"
选择一个
Email
客户端
"));
播放多媒体
:
Intent it =
new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("FILE_PATH");
it.setDataAndType(uri, "FILE_TYPE");
startActivity(it);