前言

  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

 parcelable Message; 

  例如:IGetMsg.aidl

 package com.example.aidlservicedemo.domain;

 // 这是两个自定义类
import com.example.aidlservicedemo.domain.Message;
import com.example.aidlservicedemo.domain.User; interface IGetMsg{
// 在AIDL接口中定义一个getMes方法
List<Message> getMes(in User us);
}

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对象的数组。

  例如:

     @Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
Log.i("main", "服务端Message被序列化");
dest.writeInt(id);
dest.writeString(msgText);
dest.writeString(fromName);
dest.writeString(date);
} public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() { @Override
public Message[] newArray(int size) {
return new Message[size];
} @Override
public Message createFromParcel(Parcel source) {
Log.i("main", "服务端Message被反序列化");
return new Message(source.readInt(), source.readString(),
source.readString(), source.readString());
}
};

  从上面示例中可以看出,使用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

 package com.example.aidlservicedemo.domain;

 import android.os.Parcel;
import android.os.Parcelable;
import android.text.format.DateUtils;
import android.util.Log; public class Message implements Parcelable { private int id;
private String msgText;
private String fromName;
private String date; public Message() {
super(); } public Message(int id, String msgText, String fromName, String date) {
super();
this.id = id;
this.msgText = msgText;
this.fromName = fromName;
this.date = date;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getMsgText() {
return msgText;
} public void setMsgText(String msgText) {
this.msgText = msgText;
} public String getFromName() {
return fromName;
} public void setFromName(String fromName) {
this.fromName = fromName;
} public String getDate() {
return date;
} public void setDate(String date) {
this.date = date;
} @Override
public int describeContents() {
return 0;
} @Override
public String toString() {
return "信息内容=" + msgText + ", 发件人="
+ fromName + ", 时间=" + date ;
} @Override
public void writeToParcel(Parcel dest, int flags) {
Log.i("main", "客户端Message被序列化");
dest.writeInt(id);
dest.writeString(msgText);
dest.writeString(fromName);
dest.writeString(date);
} public static final Parcelable.Creator<Message> CREATOR = new Creator<Message>() { @Override
public Message[] newArray(int size) {
return new Message[size];
} @Override
public Message createFromParcel(Parcel source) {
Log.i("main", "客户端Message被反序列化");
return new Message(source.readInt(), source.readString(),
source.readString(), source.readString());
}
};
}

Message.java

  com.example.aidlservicedemo.domain.User.aidl

 parcelable User; 

User.aidl

  com.example.aidlservicedemo.domain.User.java

 package com.example.aidlservicedemo.domain;

 import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log; public class User implements Parcelable { private int id;
private String username;
private String password;
public User() {
super();
}
public User(int id, String username, String password) {
super();
this.id = id;
this.username = username;
this.password = password;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} @Override
public boolean equals(Object o) { User us=(User)o;
if(this.username.equals(us.username)&&this.password.equals(us.password))
{
return true;
}
else
{
return false;
}
} @Override
public int describeContents() {
return 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
Log.i("main", "客户端User被序列化");
dest.writeInt(id);
dest.writeString(username);
dest.writeString(password);
}
public static final Parcelable.Creator<User> CREATOR=new Creator<User>() { @Override
public User[] newArray(int size) {
return new User[size];
} @Override
public User createFromParcel(Parcel source) {
Log.i("main", "客户端User被反序列化");
return new User(source.readInt(), source.readString(),
source.readString());
}
};
}

User.java

  服务:

  com.example.aidlservicedemo.

 package com.example.aidlservicedemo;

 import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import com.example.aidlservicedemo.domain.IGetMsg.Stub;
import com.example.aidlservicedemo.domain.Message;
import com.example.aidlservicedemo.domain.User; import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log; public class CustomTypeService extends Service {
private static final String TAG = "main";
private MsgBinder msgBinder=null;
private static Map<User, List<Message>> map=new HashMap<User, List<Message>>();
static{
for(int i=0;i<3;i++){
User user=new User(i, "jack"+i, "9999999999"+i);
List<Message> messages=new ArrayList<Message>();
Message msg=null;
if(i==0)
{
msg=new Message(i, "这两天好吗?", "Jerry", new Date().toGMTString());
messages.add(msg);
}else if(i==1)
{
msg=new Message(i, "周天去逛街吧!", "Tim", new Date().toGMTString());
messages.add(msg);
msg=new Message(i, "好无聊!", "Wesley", new Date().toGMTString());
messages.add(msg);
}
else
{
msg=new Message(i, "上次的问题解决了吗?", "Bonnie", new Date().toGMTString());
messages.add(msg);
msg=new Message(i, "明天一起吃饭吧?", "Matt", new Date().toGMTString());
messages.add(msg);
msg=new Message(i, "在哪里?", "Caroline", new Date().toGMTString());
messages.add(msg);
}
map.put(user, messages);
}
} public class MsgBinder extends Stub{ @Override
public List<Message> getMes(User us) throws RemoteException {
for(Map.Entry<User, List<Message>> msgs:map.entrySet()){
if(msgs.getKey().getUsername().equals(us.getUsername())&&msgs.getKey().getPassword().equals(us.getPassword())){
Log.i(TAG, "找到信息了");
return msgs.getValue();
}
}
Log.i(TAG, "没找到信息了");
return map.get(us);
} } @Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return msgBinder;
} @Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
msgBinder=new MsgBinder();
} @Override
public void onDestroy() {
msgBinder=null;
super.onDestroy();
} }

CustomTypeService.java

  客户端:

  com.example.aidlClientdemo.

 package com.example.aidlClientdemo;

 import java.util.List;
import java.util.Random; import com.example.aidlservicedemo.domain.IGetMsg;
import com.example.aidlservicedemo.domain.Message;
import com.example.aidlservicedemo.domain.User; import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast; public class CustomTypeActivity extends Activity {
private Button btn_startService, btn_endService, btn_getServiceData;
private IGetMsg getMsg; private static User[] users = new User[] {
new User(0, "jack0", "99999999990"),
new User(0, "jack1", "99999999991"),
new User(0, "jack2", "99999999992") }; private ServiceConnection conn = new ServiceConnection() { @Override
public void onServiceDisconnected(ComponentName name) {
getMsg = null;
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
getMsg = IGetMsg.Stub.asInterface(service);
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service); btn_startService = (Button) findViewById(R.id.btn_startService);
btn_endService = (Button) findViewById(R.id.btn_endService);
btn_getServiceData = (Button) findViewById(R.id.btn_getServiceData);
btn_startService.setOnClickListener(click);
btn_endService.setOnClickListener(click);
btn_getServiceData.setOnClickListener(click);
} private View.OnClickListener click = new OnClickListener() { @Override
public void onClick(View v) { switch (v.getId()) {
case R.id.btn_startService:
startService();
break;
case R.id.btn_endService:
endService();
break;
case R.id.btn_getServiceData:
getServiceDate();
break;
} }
}; /**
* 获取其他线程服务数据
*/
private void getServiceDate(){
try {
Random random=new Random();
int nextInt=random.nextInt(2);
List<Message> msgs=getMsg.getMes(users[nextInt]);
StringBuilder sBuilder=new StringBuilder();
for(Message msg:msgs){
sBuilder.append(msg.toString()+"\n");
}
Toast.makeText(CustomTypeActivity.this, sBuilder.toString(), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(CustomTypeActivity.this, "获取数据出错", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
} /**
* 开始服务
*/
private void startService() {
Intent service = new Intent();
service.setAction("cn.bgxt.Service.CUSTOM_TYPE_SERVICE");
bindService(service, conn, BIND_AUTO_CREATE);
Toast.makeText(CustomTypeActivity.this, "绑定服务成功", Toast.LENGTH_SHORT).show();
} /**
* 停止服务
*/
private void endService() {
unbindService(conn);
Toast.makeText(CustomTypeActivity.this, "解除绑定服务成功", Toast.LENGTH_SHORT).show();
} }

CustomTypeActivity.java

  效果展示:

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

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

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

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

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

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

  源码下载

Android--Service之AIDL传递复杂对象的更多相关文章

  1. Android service binder aidl 关系

    /********************************************************************************** * Android servic ...

  2. 【5】Android Service 与 AIDL

    前言:本系列仅介绍基本大体的使用步骤,而不对每个步骤进行细致的讲解.读者可作为已经对相关内容有所了解后的快速查阅. 一.单应用内Service的使用 Service组件与Activity以IBinde ...

  3. Android中数据的传递以及对象序列化

    Android中当两个Activity需要有信息交互的时候,可以使用Intent.具体来说: 发送单一类型数据: 发送方: String data = "Hello SecondActivi ...

  4. Android NDK 学习之传递类对象

    本博客主要是在Ubuntu 下开发,且默认你已经安装了Eclipse,Android SDK, Android NDK, CDT插件. 在Eclipse中添加配置NDK,路径如下Eclipse-> ...

  5. Android-bindService远程服务(Aidl)-传递对象

    之前上一篇讲解到本地服务,本地服务只能在自身APP中Activity访问Service,调用Service里面到方法等操作 如果想A应用访问B应用里面的方法,属于跨进程调用,如果Android不特供这 ...

  6. android Activity之间数据传递 Parcelable和Serializable接口的使用

    Activity之间传数据时,为了避免麻烦,往往会将一些值封装成对象,然后将整个对象传递过去.传对象的时候有两种情况,一种是实现Parcelable接口,一种是实现Serializable接口.0.解 ...

  7. Android学习笔记_23_服务Service之AIDL和远程服务实现进程通信以及进程间传递自定义类型参数

    一.了解AIDL语言: 在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的.因此要传递对象, 需要把对象解析 ...

  8. Android Service学习之AIDL, Parcelable和远程服务

    AIDL的作用     由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象.在Android平台,一个进程通常不能访问另一个进程的内存空 ...

  9. Android Service AIDL 远程调用服务 【简单音乐播放实例】

    Android Service是分为两种: 本地服务(Local Service): 同一个apk内被调用 远程服务(Remote Service):被另一个apk调用 远程服务需要借助AIDL来完成 ...

随机推荐

  1. java38

    字符串内存分析 String str = new String("ABCD") 会先在堆中开辟一个空间,然后去常量池(方法区)中寻找是否有该变量,如果有,直接引用常量池中的内容 如 ...

  2. reactjs 学习笔记

    1.安装 npm install -g create-react-app create-react-app my-app cd my-app npm start

  3. 剑指offer PART 2

    剑指offer PART 2 书点击自取 提取码: njku 标签(空格分隔): 笔记 C++知识点: 1.面向对象的特性 2.构造函数 3.析构函数 4.动态绑定 5.常用的设计模式 6.UML图 ...

  4. XMLHttpRequest状态码及相关事件

    1.创建一个XMLHttpRequest对象 2.对XMLHttpRequest对象进行事件的监听(定义监听事件的位置不影响 3.对XMLHttpRequest对象的状态码 状态   名称 描述 0 ...

  5. 每日一练ACM

    2019.04.15 第1000题:A+B Problem Problem DescriptionCalculate A + B. InputEach line will contain two in ...

  6. 【翻译】Flume 1.8.0 User Guide(用户指南) Channel

    翻译自官网flume1.8用户指南,原文地址:Flume 1.8.0 User Guide 篇幅限制,分为以下5篇: [翻译]Flume 1.8.0 User Guide(用户指南) [翻译]Flum ...

  7. RK3288 uboot启动流程

    VS-RK3288嵌入式板卡 U-boot 启动流程小结 bl    board_init_f -> crt0.S    initcall_run_list(init_sequence_f) - ...

  8. 转发对python装饰器的理解

    [Python] 对 Python 装饰器的理解的一些心得分享出来给大家参考   原文  http://blog.csdn.net/sxw3718401/article/details/3951958 ...

  9. Note on Preliminary Introduction to Distributed System

    今天读了几篇分布式相关的内容,记录一下.非经典论文,非系统化阅读,非严谨思考和总结.主要的着眼点在于分布式存储:好处是,跨越单台物理机器的计算和存储能力的限制,防止单点故障(single point ...

  10. Avro序列化与反序列化

    4.Avro实现序列化的步骤 1.创建maven工程,引入pom文件(定义属性和依赖信息,以及定义Avro插件) 2.根据Avro插件的要求,更改maven工程结构,需要创建一个source/main ...