zoukankan      html  css  js  c++  java
  • 【转】 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开发相关文章

      

  • 相关阅读:
    VK Cup 2012 Qualification Round 1 C. Cd and pwd commands 模拟
    VK Cup 2015
    DP总结 ——QPH
    Codeforces Round #244 (Div. 2) B. Prison Transfer 线段树rmq
    Codeforces Round #311 (Div. 2) E. Ann and Half-Palindrome 字典树/半回文串
    Codeforces Round #311 (Div. 2) D. Vitaly and Cycle 图论
    Codeforces Round #311 (Div. 2) C. Arthur and Table Multiset
    Codeforces Round #311 (Div. 2)B. Pasha and Tea 水题
    Codeforces Round #311 (Div. 2) A. Ilya and Diplomas 水题
    Codeforces Round #260 (Div. 1) D. Serega and Fun 分块
  • 原文地址:https://www.cnblogs.com/blongfree/p/5048072.html
Copyright © 2011-2022 走看看