6.1 利用Intent对象进行数据传输
意图机制被广泛应用于界面组件的调用,服务组件的调用和绑定,以及触发器组件的调用中。因此,调用组件向实现组件传递数据最自然的方式就是利用Intent对象。
Intent对象是组件间通信协议的拟定者和消息数据的传递者,它是组件间交互数据的重要载体。
如果调用组件需要向实现组件传递小数据量的附加数据,可以通过Intent对象的Extras域进行传递。在Extras中,数据以键值对的形式进行组织,这些数据会随着Intent对象一齐打包,从调用组件传递至目标组件中。
调用组件需要传递的核心数据,通常利用Intent对象的Data域进行存储,Data域中保存的是数据的URI,实现组件可以根据URI的地址去读取具体的数据内容。
作为最常用的组件间数据传输解决方案,利用Intent对象传输参数,几乎会在所有的应用程序中大量使用,用于各种组件间的通信。比如,使用Context.startActivity可以将需要浏览的地址发送给浏览器应用:
//生成需要访问地址的URI
Uri uri=Uri.parse("http://www.google.com");
//将需要访问的URI作为Data加入Intent对象,发送出去
startActivity(new Intent(Intent.ACTION_VIEW, uri));
使用Context.startService函数,可以将到期的日历事件发送给提醒服务:
//构造日历事件,把事件详情和操作放到Extra中
Intent i=new Intent();
i.setClass(context, AlertService.class);
i.putExtra("action","play");
i.putExtra("uri",calendarEventUri);
//发送事件到日历事件提醒服务
startService(i);
如果有大量数据需要传输,通常是基于URI或者文件路径进行定位,将数据存放在特定的位置,再将位置传递给目标组件,而不是直接通过Extras域将所有数据序列化传递过去,从而避免额外的序列化开销,提升组件调用的效率。比如,如果需要调用查看图片组件呈现指定的图片,不要将整个图片读取成位图对象通过Extras域传递过去,而是通过URI传入地址供查看图片组件读取访问:
Intent intent=new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
//指定图片的地址和类型,传递给目标组件
intent.setDataAndType(Uri.fromFile(file),"image/*");
startActivity(intent);
6.1.1 利用Intent对象回传数据
Intent对象,通常用于调用组件向实现组件传递数据,但在界面组件的调用中,实现组件返给调用组件的数据,也是通过Intent对象来封装的。
如图6-1所示,调用组件可以使用Activity.startActivityForResult函数发出调用请求,与Activity.startActivity函数相比,Activity.startActivityForResult增加了一个正整型数RequestCode作为操作标识,通过它可以区分不同的操作,用于处理回调事件。
当实现组件收到并完成请求后,可以调用Activity.setResult函数设置回传给调用组件的操作结果和回传数据。其中,操作结果由一个整型数来表示,Activity.RESULT_CANCEL表示操作取消,而Activity.RESULT_OK则表示操作成功;回传数据会封装成Intent对象,同样利用它的Data域和Extras域来存放具体的数据。
在实现组件处理完成后,会回退至调用组件,此时,调用组件的Activity.onActivityResult函数会被调用,将操作标识、操作结果及回传数据一并返回,开发者在此函数中可以获取并处理结果数据。
图 6-1 界面组件间的数据回传
比如,写邮件组件需要调用联系人选择组件,帮助用户完成收件人的选择,可以按如下代码发起请求:
//定义RequestCode的常量
static final int PICK_CONTACT=0;
…
//构造选择联系人的Intent对象,附加数据是联系人数据源的URI
Intent intent=new Intent(
Intent.ACTION_PICK, new Uri("content://contacts");
//发起请求
startActivityForResult(intent, PICK_CONTACT);
当联系人组件完成了联系人选择的操作后,会将用户选择的联系人数据封装成Intent对象回传给调用组件:
//构造需要回传的联系人的地址数据
Intent result=new Intent();
result.setData(new Uri("content://contacts/people/1"));
//设置返回值
setResult(RESULT_OK, result);
在写邮件组件中,需要通过派生Activity.onActivityResult函数来处理被选择的联系人:
protected void onActivityResult(
int requestCode, int resultCode, Intent data){
if(requestCode==PICK_CONTACT&&
resultCode==RESULT_OK){
//读取联系人数据
Uri selectedContact=data.getData();
…//进一步处理
}
}
与利用Intent传递数据类似,如果数据较小,可以通过Extras域进行传递,而如果数据较大,务必需要使用URI进行传递。比如,同样是请求相机组件拍照,如果期望的是小尺寸照片,会通过Extras域进行回传:
//发出Action为MediaStore.ACTION_IMAGE_CAPTURE的Intent
//如果请求时没有添加MediaStore.EXTRA_OUTPUT信息,则返回小图
//直接从返回Intent对象中名为data的Extra中获取位图对象
protected void onActivityResult(
int requestCode, int resultCode, Intent data){
final Bitmap photo=data.getParcelableExtra("data");
…
}
如果期望的是大尺寸照片,则会通过URI进行回传:
//发出Action为MediaStore.ACTION_IMAGE_CAPTURE的Intent
//如果请求时添加MediaStore.EXTRA_OUTPUT信息,则返回大图
//直接从返回Intent对象的data域中获取位图地址
protected void onActivityResult(
int requestCode, int resultCode, Intent data){
final Uri photo_uri=data.getData();
…
}