【转】 Pro Android学习笔记(八一):服务(6):复杂数据Parcel
文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处:http://blog.csdn.net/flowingflying/
在之前的StockQuote远程服务的接口中的方法为double getQuote(String ticker);。在远程服务中的方法的数据类型支持原始类型(primitive),如int这类的;支持String、CharSequence;复杂是类型支持List、Map,但在使用中有一些限制;如果我们希望使用自定义的类作为类型,需要使用Parcelable。本笔记将学习如何通过Parcelable封装在远程服务中的方法调用中进行复杂的数据传递。
远程服务实际是进程间通信,因此在接口的连接中,并不是传递对象,而是向原始类型那样,复制数据。Java的对象实际是C中的指针,Java并非没有指针,而是除了primitive类型外,全部是指针,当都是指针时,开发者感觉不到指针和非指针的差异,有时会有错误的感觉,以为Java无指针。由于不同进程有各自的内存空间,另一个进程不能操控其他进程的内存空间,也就是不能操控其他进程内存空间的对象。client和远程服务建立了AIDL接口的连接,在操作时,将数据的内容整份进行传递,类似我们在socket中传递数据,我们传递数据的地址(指针/对象)是毫无意义的,我们必须传递数据的内容。
自定义的Parcelable类
Parcel是消息(数据和对象)的容器,可以在IBinder(即远程服务的连接)中传递。Parcel是Android中设计用于高性能的IPC传输,因此我们不要将Parcel数据直接写到物理存贮中,因为Parcel中某个数据的改变,会使得其他数据变得不可读。Parcelable则是接口,我们自定义的数据类型,需要实现该接口,才能作为Parcel在IBinder中传递。
下面的例子很简单,我们自定义的数据类型Person含有两个数据,一是int age,一是String name。
public class Person implements Parcelable{
//【1】自定义的类型具体包含的数据,本例为age和name。
private int age = 0;
private String name = null;
@Override
public int describeContents() {
return 0;
}
/* 【2】要实现Parcelable接口,需要实现writeToParcel()和readFromParcel(),实现将对象(数据)写入Parcel,和从Parcel中读出对象。需要注意,写的顺序和读的顺序必须一致,因为Parcel类是快速serialization和deserialization机制,和bundle不同,没有索引机制,是线性的数据存贮和读取。
* 注意其中readFromParcel()并不是overrider,而是我们自己提供的方法,如果我们不提供,就要在
* private Person(Parcel in){
* age = in.readInt();
* name = in.readString();
* }
* 鉴于实际的数据类型会比小例子复杂,以及便于代码阅读,我们仿照writeToParcel()的命名,给出readFromParcel() */
@Override
public void writeToParcel(Parcel out, int flag) {
out.writeInt(age); //先写入age
out.writeString(name); //其次写如name
}
public void readFromParcel(Parcel in){
age = in.readInt(); //先读出age,保持与写同顺序
name = in.readString(); //其次读出name,保持与写同顺序
}
/* 【3】:提供构造函数,用于从Parcel中创建对象,也即是读的过程。这里设置为private,禁止外部调用 */
private Person(Parcel in){
readFromParcel(in);
}
// 这个构造函数,是方便我们在client中创新相关对象,并将之作为接口连接中调用方法的的参数
public Person(){
}
/* 下面是我们在自定义类中的自定义方法,本例简单提供age和name的读写 */
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
/*【4】 实现Parcelable接口的类必须要有一个static field称为CREATOR,用于实现Parcelable.Creator接口的对象。在AIDL文件自动生成的Java接口中,IBinder将调用Parcelable.Creator来获得传递对象:_arg1 = cn.wei.flowingflying.proandroidservice.Person.CREATOR.createFromParcel(data); */
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source); //见【3】
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
AIDL文件
我们定义Person.aidl对Parcelable进行说明,由于我们已经有一个Person.java,所以系统不会再自动生成相关的java代码。
package cn.wei.flowingflying.proandroidservice;
parcelable Person;
当定义了Person.aidl中,我们可以在接口定义中使用该类型。在非原语类型,非String类型,其他的类型在接口中作为参数需要描述传递的方向in、out或者inout。
package cn.wei.flowingflying.proandroidservice;
import cn.wei.flowingflying.proandroidservice.Person;
interface IStockQuoteService2{
String getQuote(in String ticker, in Person requester);
}
服务的实现
服务的实现和之前的远程服务没有什么区别,只是方法中数据类型的不同,下面是StockQuoteRemoteService2.java的片段,为了更好地和用户互动,Service会在通知栏上出现,详细可以下载我们的源代码进行查看。此外我们需要在AndroidManifest.xml中对service进行定义。
public class StockQuoteRemoteService2 extends Service{
public class StockQuoteServiceImpl extends IStockQuoteService2.Stub{
private int count = 0;
@Override
public String getQuote(String ticker, Person requester) throws RemoteException {
return "Hello " + requester.getName() + "! Quote for " + ticker + " is " + (20.0+(count++));
}
}
... ...
@Override
public IBinder onBind(Intent arg0) {
return new StockQuoteServiceImpl();
}
}
Client的实现
我们新建一个project来作为client。这个client同样需要了解接口,了解接口中所涉及的parcelable Person的定义,因此我们需要将service中的IStockQuoteService2.aidl,Person.aidl以及Person.java拷贝过来。

和之前的client只是连接接口后,调用方法的参数不同,相关代码如下:
public class MainActivity extends Activity {
private IStockQuoteService2 stockService2 = null;
… …
private ServiceConnection servConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
… …
stockService2 = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
… …
stockService2 = IStockQuoteService2.Stub.asInterface(service);
}
};
private void callService(){
try{
Person person = new Person();
person.setAge(25);
person.setName("Flowingflying");
String response = stockService2.getQuote("WEI", person);
Toast.makeText(this, response, Toast.LENGTH_LONG).show();
}catch(RemoteException e){
Log.e("Client2",e.toString());
}
}
}
同步和异步
这里学习的服务都是同步的,因为我们在UI thread中进行调用。如果service需要大量的运算,我们希望能够运行在后台,也就是client在后台线程中对服务进行调用。
本博文涉及的例子代码,可以在Pro Android学习:Android service小例子中下载。
相关链接: 我的Android开发相关文章
【转】 Pro Android学习笔记(八一):服务(6):复杂数据Parcel的更多相关文章
- 【转】 Pro Android学习笔记(六七):HTTP服务(1):HTTP GET
目录(?)[-] HTTP GET小例子 简单小例子 出现异常NetworkOnMainThreadException 通过StrictMode进行处理 URL带键值对 Andriod应用可利用ser ...
- Pro Android学习笔记 ActionBar(1):Home图标区
Pro Android学习笔记(四八):ActionBar(1):Home图标区 2013年03月10日 ⁄ 综合 ⁄ 共 3256字 ⁄ 字号 小 中 大 ⁄ 评论关闭 ActionBar在A ...
- 【转】 Pro Android学习笔记(八二):了解Package(1):包和进程
文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在之前,我们已经学习了如何签发apk,见P ...
- 【转】 Pro Android学习笔记(五五):调试和分析(3):adb命令、模拟器控制台和StrictMode
目录(?)[-] adb命令 模拟器Console StrictMode adb命令 我们在学习SQLite的使用,介绍过部分adb命令的使用,见Pro Android学习笔记(五):了解Conten ...
- 【转】 Pro Android学习笔记(五六):配置变化
目录(?)[-] Activity的destorycreate过程 Fragment的destorycreate过程 onSaveInstanceState saveFragmentInstanceS ...
- 【转】 Pro Android学习笔记(五二):ActionBar(5):list模式
可以在action bar中加入spinner的下来菜单,有关spinner,可以参考Pro Android学习笔记(二十):用户界面和控制(8):GridView和Spinner. list的样式和 ...
- 【转】 Pro Android学习笔记(四二):Fragment(7):切换效果
目录(?)[-] 利用setTransition 利用setCustomAnimations 通过ObjectAnimator自定义动态效果 程序代码的编写 利用fragment transactio ...
- 【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版
目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖 ...
- 【转】 Pro Android学习笔记(三五):Menu(6):XML方式 & PopUp菜单
目录(?)[-] 利用XML创建菜单 XML的有关属性 onClick事件 Pop-up菜单 利用XML创建菜单 在代码中对每个菜单项进行设置,繁琐且修改不灵活,不能适配多国语言的要求,可以利用资源进 ...
- 【转】 Pro Android学习笔记(三三):Menu(4):Alternative菜单
目录(?)[-] 什么是Alternative menu替代菜单 小例子说明 Alternative menu代码 关于Category和规范代码写法 关于flags 多个匹配的itemId等参数 什 ...
随机推荐
- INSPIRED启示录 读书笔记 - 第21章 产品验证
证明产品的价值.可用性.可行性 产品验证是指在正式开发.部署产品前,验证产品说明文档描述的产品是否符合预期要求 产品经理向产品团队提供最终的产品说明文档前,需要进行三项重要验证 1.可行性测试:明确在 ...
- Docker 共享存储解决方案Rex-Ray
github地址:https://github.com/rexray/rexray 安装: curl -sSL https://rexray.io/install | sh - 生成配置文件: htt ...
- linux下ipython的安装
第一种:ipython源码安装ipython的源码下载页面为:https://pypi.python.org/pypi/ipython 或者是到git页面下载:https://github.com/i ...
- shell中嵌套执行expect命令实例(利用expect实现自动登录)
expect是 #!/bin/bashpasswd='123456'/usr/bin/expect <<EOFset time 30spawn ssh root@192.168.76.10 ...
- 【P1714】切蛋糕(单调队列)
实在不明白难度等级,难不成前缀和是个很变态的东西? 说白了就是单调队列裸题,都没加什么别的东西,就是一个前缀和的计算,然而这个题也不是要用它优化,而是必须这么做啊. #include<iostr ...
- 【51nod1519】拆方块[Codeforces](dp)
题目传送门:1519 拆方块 首先,我们可以发现,如果第i堆方块被消除,只有三种情况: 1.第i-1堆方块全部被消除: 2.第i+1堆方块全部被消除:(因为两侧的方块能够保护这一堆方块在两侧不暴露) ...
- Java C++ 比较 – 虚函数、抽象函数、抽象类、接口
[转自]原文 Java – 虚函数.抽象函数.抽象类.接口 1. Java虚函数 虚函数的存在是为了多态. C++中普通成员函数加上virtual关键字就成为虚函数 Java中其实没有虚函数的概念,它 ...
- js组合模式
组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 透明方式,也就是说在Commponent中声明所有用来管理子 ...
- Codeforces 859E Desk Disorder:并查集【两个属性二选一】
题目链接:http://codeforces.com/problemset/problem/859/E 题意: 有n个人,2n个座位. 给出这n个人初始的座位,和他们想坐的座位. 每个人要么坐在原来的 ...
- R语言:导入导出数据
主要学习如何把几种常用的数据格式导入到R中进行处理,并简单介绍如何把R中的数据保存为R数据格式和csv文件. 1.保存和加载R的数据(与R.data的交互:save()函数和load()函数) a & ...