文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处: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的更多相关文章

  1. 【转】 Pro Android学习笔记(六七):HTTP服务(1):HTTP GET

    目录(?)[-] HTTP GET小例子 简单小例子 出现异常NetworkOnMainThreadException 通过StrictMode进行处理 URL带键值对 Andriod应用可利用ser ...

  2. Pro Android学习笔记 ActionBar(1):Home图标区

     Pro Android学习笔记(四八):ActionBar(1):Home图标区 2013年03月10日 ⁄ 综合 ⁄ 共 3256字 ⁄ 字号 小 中 大 ⁄ 评论关闭 ActionBar在A ...

  3. 【转】 Pro Android学习笔记(八二):了解Package(1):包和进程

    文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在之前,我们已经学习了如何签发apk,见P ...

  4. 【转】 Pro Android学习笔记(五五):调试和分析(3):adb命令、模拟器控制台和StrictMode

    目录(?)[-] adb命令 模拟器Console StrictMode adb命令 我们在学习SQLite的使用,介绍过部分adb命令的使用,见Pro Android学习笔记(五):了解Conten ...

  5. 【转】 Pro Android学习笔记(五六):配置变化

    目录(?)[-] Activity的destorycreate过程 Fragment的destorycreate过程 onSaveInstanceState saveFragmentInstanceS ...

  6. 【转】 Pro Android学习笔记(五二):ActionBar(5):list模式

    可以在action bar中加入spinner的下来菜单,有关spinner,可以参考Pro Android学习笔记(二十):用户界面和控制(8):GridView和Spinner. list的样式和 ...

  7. 【转】 Pro Android学习笔记(四二):Fragment(7):切换效果

    目录(?)[-] 利用setTransition 利用setCustomAnimations 通过ObjectAnimator自定义动态效果 程序代码的编写 利用fragment transactio ...

  8. 【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版

    目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖 ...

  9. 【转】 Pro Android学习笔记(三五):Menu(6):XML方式 & PopUp菜单

    目录(?)[-] 利用XML创建菜单 XML的有关属性 onClick事件 Pop-up菜单 利用XML创建菜单 在代码中对每个菜单项进行设置,繁琐且修改不灵活,不能适配多国语言的要求,可以利用资源进 ...

  10. 【转】 Pro Android学习笔记(三三):Menu(4):Alternative菜单

    目录(?)[-] 什么是Alternative menu替代菜单 小例子说明 Alternative menu代码 关于Category和规范代码写法 关于flags 多个匹配的itemId等参数 什 ...

随机推荐

  1. 斯坦福机器学习视频笔记 Week9 异常检测和高斯混合模型 Anomaly Detection

    异常检测,广泛用于欺诈检测(例如“此信用卡被盗?”). 给定大量的数据点,我们有时可能想要找出哪些与平均值有显着差异. 例如,在制造中,我们可能想要检测缺陷或异常. 我们展示了如何使用高斯分布来建模数 ...

  2. JMeter学习(七)聚合报告之 90% Line 正确理解

    90% Line 参数正确的含义: 虽然,我的上面理解有一定的道理,显然它是错误的.那看看JMeter 官网是怎么说的? 90% Line - 90% of the samples took no m ...

  3. json前后台传输,以及乱码中文问题探讨

    背景介绍: 我现在的工作是做传统项目开发,没有用到框架.最近在做项目时,经常需要使用ajax从后台拿数据到前台,是json格式的.先说下我在项目中遇到的问题吧,前台拿到了数据,需要将其转化为对象,我使 ...

  4. 算法总结之 构造数组MaxTree

    一个数组的MaxTree定义如下: 数组必须没有重复元素 MaxTree是一颗二叉树,数组的每一个值对应一个二叉树的节点 包括MaxTre树在内且在其中的每一颗子树上,值最大的节点都是树的头 给定一个 ...

  5. 元素 "context:component-scan" 的前缀 "context" 未绑定的解决方案

    在动态web项目(Dynamic Web Project)中,使用SpringMVC框架,新建Spring的配置文件springmvc.xml,添加扫描控制器 <context:componen ...

  6. 深入理解Servlet3.0异步请求

    异步请求的基础概念 异步请求最直接的用法就是处理耗时业务,Http协议是单向的,只能客户端拉不能服务器主推. 异步请求的核心原理主要分为两大类:1.轮询.2长连接 轮询:就是定时获取返回结果. 长连接 ...

  7. 第一个Python程序hello.py提示出现File "<stdin>",line 1错误

    写第一个Python程序hello.py,内容仅有一句,print 'hello world', 运行 Python hello.py 出错,提示: File "<stdin>& ...

  8. Composer如何安装(安装注意事项)

    Composer如何安装(安装注意事项) 一.总结 一句话总结:安装的时候主要看安装错误提示: 常见的错误有: a.php需要开启openssl配置.我们打开php目录下的php.ini.将opens ...

  9. Python中的Unicode编码和UTF-8编码

    下午看廖雪峰的Python2.7教程,看到 字符串和编码 一节,有一点感受,结合崔庆才的Python博客 ,把这种感受记录下来: ASCII码:是用一个字节(8bit, 0-255)中的127个字母表 ...

  10. Windows 运行中的命令

    辅助功能选项 access.cpl 添加硬件向导 hdwwiz.cpl 添加或删除程序 appwiz.cpl 管理工具 control admintools 自动更新 wuaucpl.cpl Blue ...