zoukankan      html  css  js  c++  java
  • Android--Service之AIDL传递复杂对象

    前言

      Android的AIDL不仅可以在绑定服务中传递一些Android规定的数据类型的数据,还可以传递一些复杂类型的数据。但是与传递系统允许的数据类型相比,复杂类型数据的传递要做更多的工作,本篇博客就讲解一下如何使用AIDL接口通过绑定服务在进程间传递数据。关于AIDL传递规定类型数据的内容,不了解的朋友可以看看之前的博客: AIDL传递系统允许类型数据

      本篇博客的主要内容:

    1. AIDL传递复杂类型对象的特殊处理
    2. Parcelable与Parcel接口
    3. 实现Parcelable接口
    4. AIDL传递复杂类型对象Demo
    5. AIDL传递对象序列化过程详解

    AIDL传递复杂类型对象的特殊处理 

      前面已经介绍了通过AIDL接口在进程间传递系统允许的数据,如果需要传递一个复杂类型的对象,就没那么简单了,需要额外做一些处理。如下:

    1. 定义数据接口的AIDL文件中,使用parcelable关键字,例如:parcelable Message;
    2. 在其数据实现类中实现Parcelable接口,并实现对应的方法。
    3. 在业务接口的AIDL文件中,使用import引入数据接口AIDL的包名。

      例如:Message.aidl

    1 parcelable Message; 

      例如:IGetMsg.aidl

    复制代码
     1 package com.example.aidlservicedemo.domain;
     2 
     3 // 这是两个自定义类
     4 import com.example.aidlservicedemo.domain.Message;
     5 import com.example.aidlservicedemo.domain.User;
     6 
     7 interface IGetMsg{
     8     // 在AIDL接口中定义一个getMes方法
     9     List<Message> getMes(in User us);
    10 }
    复制代码

    Parcelable与Parcel接口

      先来说说Android对象序列化,在Android中序列化对象主要有两种方式,实现Serializable接口或是实现Parcelable接口。Serializable接口是JavaSE原生支持的,而Parcelable接口是Android所特有的,它的序列化和反序列化的效率均比Serializable接口高,而AIDL进行在进程间通信(IPC),就是需要实现这个Parcelable接口。

      Parcelable接口的作用:实现了Parcelable接口的实例,可以将自身的数据信息写入一个Parcel对象,也可以从parcel中恢复到对象的状态。而Parcel就是完成数据序列化写入的载体。

      上面提到Parcel,再来聊聊Parcel是什么?Android系统设计之初,定位就是针对内存受限的设备,因此对性能要求更好,所以系统中采用进程间通信(IPC)机制,必然要求性能更优良的序列化方式,所以Parcel就被设计出来了,其定位就是轻量级的高效的对象序列化机制与反序列化机制。如果读一下Android的底层代码,会发现Parcel是使用C++实现的,底层直接通过Parcel指针操作内存实现,所以它的才更高效。

      Parcel也提供了一系列的方法帮助写入数据与读取数据,这里简单介绍一下:

    • obtain():在池中获取一个新的Parcel。
    • dataSize():得到当前Parcel对象的实际存储空间。
    • dataPostion():获取当前Parcel对象的偏移量。
    • setDataPosition():设置当前Parcel对象的偏移量。
    • recyle():清空、回收当前Parcel对象的内存。
    • writeXxx():向当前Parcel对象写入数据,具有多种重载。
    • readXxx():从当前Parcel对象读取数据,具有多种重载。

      简单来说,Parcelable通过writeToParcel()方法,对复杂对象的数据写入Parcel的方式进行对象序列化,然后在需要的时候,通过其内定义的静态属性CREATOR.createFromParcel()进行反序列化的操作。Parcelable对Parcel进行了包装,其内部就是通过操作Parcel进行序列化与反序列化的。

      Parcelable与Parcel均定义在android.os包下,而这种机制不仅用于AIDL,还可以用于Intent传递数据等其他地方,这不是本篇博客的主题,以后用到再详细介绍。

    实现Parcelable接口

      定义好数据接口的AIDL文件后,需要定义一个数据实现类,实现Parcelable接口,并实现对应的方法,Parcelable有如下几个必须要实现的抽象方法:

    • abstract int describeContents():返回一个位掩码,表示一组特殊对象类型的Parcelable,一般返回0即可。
    • asbtract void writeToParcel(Parcel dest,int flags):实现对象的序列化,通过Parcel的一系列writeXxx()方法序列化对象。

      除了上面两个方法,还需要在实现类中定义一个名为"CREATOR",类型为"Parcelable.Creator<T>"的泛型静态属性,它实现了对象的反序列化。它也有两个必须实现的抽象方法:

    • abstract T createFromParcel(Parcel source):通过source对象,根据writeToParcel()方法序列化的数据,反序列化一个Parcelable对象。
    • abstract T[] newArray(int size):创建一个新的Parcelable对象的数组。

      例如:

    复制代码
     1     @Override
     2     public int describeContents() {
     3         return 0;
     4     }
     5     
     6     @Override
     7     public void writeToParcel(Parcel dest, int flags) {
     8         Log.i("main", "服务端Message被序列化");
     9         dest.writeInt(id);
    10         dest.writeString(msgText);
    11         dest.writeString(fromName);
    12         dest.writeString(date);
    13     }
    14 
    15     public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {
    16 
    17         @Override
    18         public Message[] newArray(int size) {
    19             return new Message[size];
    20         }
    21 
    22         @Override
    23         public Message createFromParcel(Parcel source) {
    24             Log.i("main", "服务端Message被反序列化");
    25             return new Message(source.readInt(), source.readString(),
    26                     source.readString(), source.readString());
    27         }
    28     };
    复制代码

      从上面示例中可以看出,使用writeToParcel()方法进行序列化,通过CREATOR.createFromParcel进行反序列化,它们都传递一个Parcel类型的对象,这里要注意的是两个方法中Parcel对象的writeXxx()和readXxx()方法的顺序必须一致,因为一般序列化数据是以链的形式序列化的,如果顺序不对,反序列化的数据会出错。

    AIDL传递复杂类型对象Demo

       关键点已经讲到, 下面通过一个简单的Demo来演示AIDL传递复杂对象。

      AIDL接口:

      com.example.aidlservicedemo.domain.Message.aidl

     Message.aidl

      com.example.aidlservicedemo.domain.Message.java

     Message.java

      com.example.aidlservicedemo.domain.User.aidl

     User.aidl

      com.example.aidlservicedemo.domain.User.java

     User.java

      服务:

      com.example.aidlservicedemo.

     CustomTypeService.java

      客户端:

      com.example.aidlClientdemo.

     CustomTypeActivity.java

      效果展示:

    AIDL传递对象序列化过程详解

      通过上面Demo打印的日志,解释一下序列化的过程,打开Logcat查看日志。

      从上图的PID列可以看出这是两个线程间的交互。

      流程是这样的,客户端传递一个User对象给服务端,服务端通过User对象处理数据,返回两个Message对象给客户端。

      首先,在客户端传递给服务端User对象前,客户端先把User对象序列化,然后传递给服务端之后,服务端接收到的是一段序列化后的数据,它再按照原定的规则对数据进行反序列化,然后处理User。当服务端查到这个User有两条Message时,需要传递这两条Message对象给客户端,在传递前对Message对象进行序列化,客户端收到服务端传递过来的序列化后的数据,再根据既定的规则进行反序列化,得到正确的对象。

      从这个流程可以看出,在进程间传递的数据必定是被序列化过的,否则无法传递。而对于那些AIDL默认允许传递的数据类型(int、double、String、List等),它们其实内部已经实现了序列化,所以无需我们再去指定序列化规则。但是对于复杂类型对象而言,系统无法知道如何去序列化与反序列化,所以需要我们指定规则。

      源码下载

  • 相关阅读:
    poj 2425 AChessGame(博弈)
    poj2975 Nim 胜利的方案数
    hdu 5724 SG+状态压缩
    hdu 5274 Dylans loves tree(LCA + 线段树)
    hdu 5266 pog loves szh III(lca + 线段树)
    hdu 4031 attack 线段树区间更新
    51 nod 1188 最大公约数之和 V2
    51nod 1040 最大公约数之和(欧拉函数)
    51nod 1035:最长的循环节
    Nim游戏(组合游戏Combinatorial Games)
  • 原文地址:https://www.cnblogs.com/shitaotao/p/7635728.html
Copyright © 2011-2022 走看看