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

    parcelable Message; 
    Message.aidl

      com.example.aidlservicedemo.domain.Message.java

     1 package com.example.aidlservicedemo.domain;
     2 
     3 import android.os.Parcel;
     4 import android.os.Parcelable;
     5 import android.text.format.DateUtils;
     6 import android.util.Log;
     7 
     8 public class Message implements Parcelable {
     9 
    10     private int id;
    11     private String msgText;
    12     private String fromName;
    13     private String date;
    14 
    15     public Message() {
    16         super();
    17 
    18     }
    19 
    20     public Message(int id, String msgText, String fromName, String date) {
    21         super();
    22         this.id = id;
    23         this.msgText = msgText;
    24         this.fromName = fromName;
    25         this.date = date;
    26     }
    27 
    28     public int getId() {
    29         return id;
    30     }
    31 
    32     public void setId(int id) {
    33         this.id = id;
    34     }
    35 
    36     public String getMsgText() {
    37         return msgText;
    38     }
    39 
    40     public void setMsgText(String msgText) {
    41         this.msgText = msgText;
    42     }
    43 
    44     public String getFromName() {
    45         return fromName;
    46     }
    47 
    48     public void setFromName(String fromName) {
    49         this.fromName = fromName;
    50     }
    51 
    52     public String getDate() {
    53         return date;
    54     }
    55 
    56     public void setDate(String date) {
    57         this.date = date;
    58     }
    59 
    60     @Override
    61     public int describeContents() {
    62         return 0;
    63     }
    64 
    65     @Override
    66     public String toString() {
    67         return "信息内容=" + msgText + ", 发件人="
    68                 + fromName + ", 时间=" + date ;
    69     }
    70 
    71     @Override
    72     public void writeToParcel(Parcel dest, int flags) {
    73         Log.i("main", "客户端Message被序列化");
    74         dest.writeInt(id);
    75         dest.writeString(msgText);
    76         dest.writeString(fromName);
    77         dest.writeString(date);
    78     }
    79 
    80     public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() {
    81 
    82         @Override
    83         public Message[] newArray(int size) {
    84             return new Message[size];
    85         }
    86 
    87         @Override
    88         public Message createFromParcel(Parcel source) {
    89             Log.i("main", "客户端Message被反序列化");
    90             return new Message(source.readInt(), source.readString(),
    91                     source.readString(), source.readString());
    92         }
    93     };
    94 }
    Message.java

      com.example.aidlservicedemo.domain.User.aidl

    1 parcelable User; 
    User.aidl

      com.example.aidlservicedemo.domain.User.java

     1 package com.example.aidlservicedemo.domain;
     2 
     3 import android.os.Parcel;
     4 import android.os.Parcelable;
     5 import android.util.Log;
     6 
     7 public class User implements Parcelable {
     8     
     9     
    10     private int id;
    11     private String username;
    12     private String password;
    13     public User() {
    14         super();
    15     }
    16     public User(int id, String username, String password) {
    17         super();
    18         this.id = id;
    19         this.username = username;
    20         this.password = password;
    21     }
    22 
    23     public int getId() {
    24         return id;
    25     }
    26 
    27     public void setId(int id) {
    28         this.id = id;
    29     }
    30 
    31     public String getUsername() {
    32         return username;
    33     }
    34 
    35     public void setUsername(String username) {
    36         this.username = username;
    37     }
    38 
    39     public String getPassword() {
    40         return password;
    41     }
    42 
    43     public void setPassword(String password) {
    44         this.password = password;
    45     }
    46 
    47     @Override
    48     public boolean equals(Object o) {
    49 
    50         User us=(User)o;
    51         if(this.username.equals(us.username)&&this.password.equals(us.password))
    52         {
    53             return true;
    54         }
    55         else
    56         {
    57             return false;
    58         }
    59     }
    60     
    61     @Override
    62     public int describeContents() {
    63         return 0;
    64     }
    65 
    66     @Override
    67     public void writeToParcel(Parcel dest, int flags) {
    68         Log.i("main", "客户端User被序列化");
    69         dest.writeInt(id);
    70         dest.writeString(username);
    71         dest.writeString(password);
    72     }
    73     public static final Parcelable.Creator<User> CREATOR=new Creator<User>() {
    74 
    75         @Override
    76         public User[] newArray(int size) {
    77             return new User[size];
    78         }
    79 
    80         @Override
    81         public User createFromParcel(Parcel source) {
    82             Log.i("main", "客户端User被反序列化");
    83             return new User(source.readInt(), source.readString(),
    84                     source.readString());
    85         }
    86     };
    87 }
    User.java

      服务:

      com.example.aidlservicedemo.

     1 package com.example.aidlservicedemo;
     2 
     3 import java.util.ArrayList;
     4 import java.util.Date;
     5 import java.util.HashMap;
     6 import java.util.List;
     7 import java.util.Map;
     8 
     9 import com.example.aidlservicedemo.domain.IGetMsg.Stub;
    10 import com.example.aidlservicedemo.domain.Message;
    11 import com.example.aidlservicedemo.domain.User;
    12 
    13 import android.app.Service;
    14 import android.content.Intent;
    15 import android.os.IBinder;
    16 import android.os.RemoteException;
    17 import android.util.Log;
    18 
    19 public class CustomTypeService extends Service {
    20     private static final String TAG = "main";
    21     private MsgBinder msgBinder=null;
    22     private static Map<User, List<Message>> map=new HashMap<User, List<Message>>();
    23     static{
    24         for(int i=0;i<3;i++){
    25             User user=new User(i, "jack"+i, "9999999999"+i);
    26             List<Message> messages=new ArrayList<Message>();
    27             Message msg=null;
    28             if(i==0)
    29             {
    30                 msg=new Message(i, "这两天好吗?", "Jerry", new Date().toGMTString());
    31                 messages.add(msg);
    32             }else if(i==1)
    33             {
    34                 msg=new Message(i, "周天去逛街吧!", "Tim", new Date().toGMTString());
    35                 messages.add(msg);
    36                 msg=new Message(i, "好无聊!", "Wesley", new Date().toGMTString());
    37                 messages.add(msg);
    38             }
    39             else
    40             {
    41                 msg=new Message(i, "上次的问题解决了吗?", "Bonnie", new Date().toGMTString());
    42                 messages.add(msg);
    43                 msg=new Message(i, "明天一起吃饭吧?", "Matt", new Date().toGMTString());
    44                 messages.add(msg);
    45                 msg=new Message(i, "在哪里?", "Caroline", new Date().toGMTString());
    46                 messages.add(msg);
    47             }
    48             map.put(user, messages);
    49         }
    50     }
    51     
    52     public class MsgBinder extends Stub{
    53 
    54         @Override
    55         public List<Message> getMes(User us) throws RemoteException {            
    56             for(Map.Entry<User, List<Message>> msgs:map.entrySet()){
    57                 if(msgs.getKey().getUsername().equals(us.getUsername())&&msgs.getKey().getPassword().equals(us.getPassword())){
    58                     Log.i(TAG, "找到信息了");
    59                     return msgs.getValue();
    60                 }
    61             }
    62             Log.i(TAG, "没找到信息了");
    63             return map.get(us);
    64         }
    65         
    66     }
    67     
    68     @Override
    69     public IBinder onBind(Intent intent) {
    70         // TODO Auto-generated method stub
    71         return msgBinder;
    72     }
    73 
    74     @Override
    75     public void onCreate() {
    76         // TODO Auto-generated method stub
    77         super.onCreate();
    78         msgBinder=new MsgBinder();
    79     }
    80 
    81     @Override
    82     public void onDestroy() {
    83         msgBinder=null;
    84         super.onDestroy();
    85     }
    86 
    87 }
    CustomTypeService.java

      客户端:

      com.example.aidlClientdemo.

      1 package com.example.aidlClientdemo;
      2 
      3 import java.util.List;
      4 import java.util.Random;
      5 
      6 import com.example.aidlservicedemo.domain.IGetMsg;
      7 import com.example.aidlservicedemo.domain.Message;
      8 import com.example.aidlservicedemo.domain.User;
      9 
     10 import android.app.Activity;
     11 import android.content.ComponentName;
     12 import android.content.Intent;
     13 import android.content.ServiceConnection;
     14 import android.os.Bundle;
     15 import android.os.IBinder;
     16 import android.os.RemoteException;
     17 import android.view.View;
     18 import android.view.View.OnClickListener;
     19 import android.widget.Button;
     20 import android.widget.Toast;
     21 
     22 public class CustomTypeActivity extends Activity {
     23     private Button btn_startService, btn_endService, btn_getServiceData;
     24     private IGetMsg getMsg;
     25 
     26     private static User[] users = new User[] {
     27             new User(0, "jack0", "99999999990"),
     28             new User(0, "jack1", "99999999991"),
     29             new User(0, "jack2", "99999999992") };
     30 
     31     private ServiceConnection conn = new ServiceConnection() {
     32 
     33         @Override
     34         public void onServiceDisconnected(ComponentName name) {
     35             getMsg = null;
     36         }
     37 
     38         @Override
     39         public void onServiceConnected(ComponentName name, IBinder service) {
     40             getMsg = IGetMsg.Stub.asInterface(service);
     41         }
     42     };
     43 
     44     @Override
     45     protected void onCreate(Bundle savedInstanceState) {
     46         super.onCreate(savedInstanceState);
     47         setContentView(R.layout.activity_service);
     48 
     49         btn_startService = (Button) findViewById(R.id.btn_startService);
     50         btn_endService = (Button) findViewById(R.id.btn_endService);
     51         btn_getServiceData = (Button) findViewById(R.id.btn_getServiceData);
     52         btn_startService.setOnClickListener(click);
     53         btn_endService.setOnClickListener(click);
     54         btn_getServiceData.setOnClickListener(click);
     55     }
     56 
     57     private View.OnClickListener click = new OnClickListener() {
     58 
     59         @Override
     60         public void onClick(View v) {
     61 
     62             switch (v.getId()) {
     63             case R.id.btn_startService:
     64                 startService();
     65                 break;
     66             case R.id.btn_endService:
     67                 endService();
     68                 break;
     69             case R.id.btn_getServiceData:
     70                 getServiceDate();
     71                 break;
     72             }
     73 
     74         }
     75     };
     76 
     77     /**
     78      * 获取其他线程服务数据
     79      */
     80     private void getServiceDate(){
     81         try {
     82             Random random=new Random();        
     83             int nextInt=random.nextInt(2);
     84             List<Message> msgs=getMsg.getMes(users[nextInt]);
     85             StringBuilder sBuilder=new StringBuilder();
     86             for(Message msg:msgs){
     87                 sBuilder.append(msg.toString()+"
    ");
     88             }
     89             Toast.makeText(CustomTypeActivity.this, sBuilder.toString(), Toast.LENGTH_SHORT).show();
     90         } catch (Exception e) {
     91             Toast.makeText(CustomTypeActivity.this, "获取数据出错", Toast.LENGTH_SHORT).show();
     92             e.printStackTrace();
     93         }
     94     }
     95 
     96     /**
     97      * 开始服务
     98      */
     99     private void startService() {
    100         Intent service = new Intent();
    101         service.setAction("cn.bgxt.Service.CUSTOM_TYPE_SERVICE");
    102         bindService(service, conn, BIND_AUTO_CREATE);
    103         Toast.makeText(CustomTypeActivity.this, "绑定服务成功", Toast.LENGTH_SHORT).show();
    104     }
    105 
    106     /**
    107      * 停止服务
    108      */
    109     private void endService() {
    110         unbindService(conn);
    111         Toast.makeText(CustomTypeActivity.this, "解除绑定服务成功", Toast.LENGTH_SHORT).show();
    112     }
    113 
    114 }
    CustomTypeActivity.java

      效果展示:

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

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

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

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

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

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

      源码下载

  • 相关阅读:
    Eclipse 导入项目乱码问题(中文乱码)
    sql中视图视图的作用
    Java基础-super关键字与this关键字
    Android LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)的参数理解
    Android View和ViewGroup
    工厂方法模式(java 设计模式)
    设计模式(java) 单例模式 单例类
    eclipse乱码解决方法
    No resource found that matches the given name 'Theme.AppCompat.Light 的完美解决方案
    【转】使用 Eclipse 调试 Java 程序的 10 个技巧
  • 原文地址:https://www.cnblogs.com/plokmju/p/android_Service_aidl_CustomType.html
Copyright © 2011-2022 走看看