1.AIDL文本解释

在软件工程中,接口定义语言(IDL)已经成为通用术语,是用来描述软件组件接口的特定语言。在Android中,该IDL被称为Android接口定义语言(AIDL),它是纯文本文件,使用Java类似语法编写。但是,编写Java接口的编写AIDL文件还有有些不同的。

首先,对所有的非原始类型参数,需要指定如下三种类型方向指示符之一:in,out,inout。in类型方向指示符只用于输入,客户端不会看到Service对对象的修改。out类型表明输入对象不包含相关的数据,但会由Service生成相关的数据。inout类型是上面两种类型的结合。切记只使用需要的类型,因为每种类型都有相应的消耗。

另一个需要记住的是,所有用于通信的自定义类都需要创建一个AIDL文件,是用来声明该类实现了Parcelable接口。

2.Parcel

Binder事务通常会传递事务数据。这种数据被称为parcel(包裹)。Android提供了相应的API。允许开发者为大多数Java对象创建parcel。

Android中的parcel和JAVA SE中的序列化对象类似。不同之处在于,开发者需要使用parcelable实现对象的编解码工作。该接口定义了两个编写Parcel对象的方法,以及一个静态的不可被复写的Creator对象,该对象用来从Parcel中读取相应的对象。如下所示:

public class CustomData implements Parcelable {
public static final Creator<CustomData> CREATOR=new Creator<CustomData>(){
@Override
public CustomData createFromParcel(Parcel source) {
CustomData customData=new CustomData();
customData.mName=source.readString();
customData.mReferences=new ArrayList<>();
source.readStringList(customData.mReferences);
customData.mCreated=new Date(source.readLong());
return customData;
} @Override
public CustomData[] newArray(int size) {
return new CustomData[size];
}
};
private String mName;
private List<String> mReferences;
private Date mCreated;
public CustomData(){
this.mName="";//默认为空字符串
this.mReferences=new ArrayList<String>();
this.mCreated=new Date();//默认为当前时间
} public Date getmCreated() {
return mCreated;
} public List<String> getmReferences() {
return mReferences;
} public String getmName() {
return mName;
} public void setmName(String mName) {
this.mName = mName;
} public void setmReferences(List<String> mReferences) {
this.mReferences = mReferences;
} public void setmCreated(Date mCreated) {
this.mCreated = mCreated;
} @Override
public int describeContents() {
return ;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.mName);
dest.writeStringList(this.mReferences);
dest.writeLong(this.mCreated.getTime());
} @Override
public boolean equals(Object o) {
if(this==o)return true;
if(o==null || getClass()!=o.getClass())return false;
CustomData that=(CustomData)o;
return this.mCreated.equals(that.mCreated) && this.mName.equals(that.mName);
} @Override
public int hashCode() {
int result=this.mName.hashCode();
result=*result+this.mCreated.hashCode();
return result;
}
}

前面的代码显示了实现Parcelable接口的CustomData类。注意CREATOR成员对象的实现,以及createFromParcel()如何使用Parcel.readStringList()方法来读取整个List对象,而不需要指定列表的长度,Parcel对象内部会处理这种情况。

实现该接口后就可以通过Binder IPC在应用间发送该类的对象了。

3.Binder简介

Binder IPC通信示意图:

Binder通讯遵循客户端-服务器模式。客户端使用客户端代理来处理与内核驱动程序的通信。在服务器端,Binder框架维护了一系列Binder线程。内核驱动会使用服务器端的Binder线程把消息从客户端代理分发给接受对象。这一点需要特别注意,因为当通过Binder接受Service调用时,它们并不会运行在应用程序的主线程上。这样一来,客户端到远程Service的连接就不会阻塞应用的主线程。

开发者使用Binder基类以及IBinder实现Binder机制。Service组件的Service.onBind()方法会返回实现IBinder接口的类。当Service发布远程API时,开发者通常使用AIDL文件生成IBinder类。

使用Binder通信时,客户端需要知道远程Binder对象的地址。然而,Binder的设计要求只有实现类,比如要调用的Service才知道地址。开发者使用Intent解析来进行Binder寻址。客户端使用action字符串或者组件名来构建Intent对象,然而使用它初始化与远程应用程序的通信,然而,Intent只是实际Binder地址的抽象描述,为了能够建立通信,还需要翻译成实际的地址。

ServiceManager是一个特殊的Binder节点,它巡行在Android系统服务内,管理所有的地址解析,是唯一一个有全局地址的Binder节点。因为所有的Android组件都使用Binder进行通信,它们需要使用ServiceManager进行注册,通过如上所述的地址来进行通信。

客户端想要和Service或者其他组件通信,需要隐式地通过Intent查询ServiceManager来接受Binder地址。

4. 完整是实例

AIDL设计到的知识都在前面做了详细的介绍,下面将跑一跑代码验证其可行性。

下面的代码片段是一个名为CustomData.aidl的AIDL文件示例。它应该和Java元代码文件放在同一个包里。

package com.example.liyuanjing.myapplication;parcelable CustomData;

然后需要在AIDL文件中引入所有需要自定义的类,如下所示:

package com.example.liyuanjing.myapplication;
import com.example.liyuanjing.myapplication.CustomData;
interface ApiInterfaceV1 {
boolean isPrime(long value);//用于检查数字是否为素数的简单远程方法
void getAllDataSince(long timestamp, out CustomData[] result);//检索timestamp以后的所有CustomData对象,至多获取result.length个对象
void storeData(in CustomData data);//存储CustomData对象 
}

写完上面的代码,忘记提醒开发者,原始类型参数不需要方法指示符。

切记,一旦实现了客户端代码,就不能在修改或者移除AIDL文件中的方法。可以在文件末尾添加新的方法,但是因为AIDL编译器会为每个方法生成标识符,所以不能修改现存的方法,否则不能向后兼容老版本。需要处理新版的API时,建议创建一个新的AIDL文件。这样做允许保持与老版本客户端的兼容。正如上面ApiInterfaceV1文件名,在天际新方法就可以创建一个V2结尾的文件,以此类推。

准备好AIDL文件后,服务器端需要实现

public class AidlService extends Service {
private ArrayList<CustomData> mCustomDataCollection;
@Override
public void onCreate() {
super.onCreate();
this.mCustomDataCollection=new ArrayList<CustomData>();
} @Override
public IBinder onBind(Intent intent) {
return mBinder;
} private void getDataSinceImpl(CustomData[] result,Date since){
int size=this.mCustomDataCollection.size();
int pos=;
for (int i=;i<size && pos<result.length;i++){
CustomData storedValue=this.mCustomDataCollection.get(i);
if(since.after(storedValue.getmCreated())){
result[pos++]=storedValue;
}
}
} private void storeDataImpl(CustomData data){
int size=this.mCustomDataCollection.size();
for (int i=;i<size;i++){
CustomData customData=this.mCustomDataCollection.get(i);
if(customData.equals(data)){
this.mCustomDataCollection.set(i,data);
return ;
}
}
this.mCustomDataCollection.add(data);
} public static boolean isPrimeImpl(long number){
return true;
}
private final ApiInterfaceV1.Stub mBinder=new ApiInterfaceV1.Stub(){
@Override
public boolean isPrime(long value) throws RemoteException {
return isPrimeImpl(value);
} @Override
public void getAllDataSince(long timestamp, CustomData[] result) throws RemoteException {
getDataSinceImpl(result,new Date(timestamp));
} @Override
public void storeData(CustomData data) throws RemoteException {
storeDataImpl(data);
}
};
}

上面的代码中Service在代码末尾实现了ApiInterfaceV1.Stub.该对象也会在onBind()方法中返回给绑定到Service的客户端。注意,每次对ServiceAPI的调用都运行在自身的线程上,因为Binder提供了一个线程池用于执行所有的客户端调用。这意味着使用这种方法时,客户端不会阻塞Service所属的主线程.

服务器端配置文件配置服务如下:

<service
android:name=".AidlService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.example.liyuanjing.myapplication.AIDL_SERVICE"/>
</intent-filter>
</service>

android:exported="true"表示能被其他应用程序调用

android:enabled="true" 服务处于激活状态

以上是服务器端所需要的全部代码

下面的Activity展示了如何绑定到一个远程Service以及检索ApiInterfaceV1接口。如果是该API的唯一用户,可以同时管理客户端和服务器端的版本,那么这是首选的解决方案。

public class MainActivity extends Activity implements ServiceConnection {
private Button start;
private ApiInterfaceV1 mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.start=(Button)findViewById(R.id.start);
}
@Override
protected void onResume() {
super.onResume();
bindService(new Intent("com.example.liyuanjing.myapplication.AIDL_SERVICE"),this,BIND_AUTO_CREATE);
} @Override
protected void onPause() {
super.onPause();
unbindService(this);
} public void onCheckForPrime(View v)throws Exception{
long number=;
boolean isPrime=mService.isPrime(number);
String message=isPrime?"你知道的这是正确啊":"你不知道的这是错误啊";
Toast.makeText(this,message, Toast.LENGTH_LONG).show();
} @Override
public void onServiceConnected(ComponentName name, IBinder service) {
this.mService=ApiInterfaceV1.Stub.asInterface(service);
this.start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
onCheckForPrime(v);
} catch (Exception e) {
e.printStackTrace();
}
}
});
} @Override
public void onServiceDisconnected(ComponentName name) {
this.mService=null;
}
}
当然客户端只有上面的代码是远远不能调用服务器端的Service。需要将编译后的AIDL文件和Parcel包裹拷贝到客户端下,并且包的名称要和服务器端的一致否则会出现错误提示。
编译后的AIDL文件在如下图所示的路径中:
客户端拷贝后的项目缩略图如下图所示:
	
一定要注意从服务器拷贝文件到客户端中包名必须一样否则不仅不能实现你所要的功能,而且会报错。

版权声明:本文为博主原创文章,未经博主允许不得转载。

AIDL实现Android IPC的更多相关文章

  1. Android IPC通信和AIDL技术应用

    首先我们了解一下 IPC和AIDL IPC:进程间通信 AIDL:Android Interface Definition Language,即Android接口定义语言. 为什么使用: Androi ...

  2. Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

    在上一篇文章Android IPC机制(二)用Messenger进行进程间通信中我们介绍了使用Messenger来进行进程间通信的方法.可是我们能发现Messenger是以串行的方式来处理client ...

  3. 【Android - IPC】之AIDL简介

    参考资料: 1.<Android开发艺术探索>第二章2.4.4 2.Android AIDL Binder框架解析:http://blog.csdn.net/lmj623565791/ar ...

  4. Android IPC机制之AIDL

    什么是AIDL AIDL:Android Interface Definition Language,即Android接口定义语言. Android系统中的进程之间不能共享内存,因此,需要提供一些机制 ...

  5. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- AIDL

    服务端: 最终项目结构: 这个项目中,我们将用到自定义类CustomData作为服务端与客户端传递的数据. Step 1:创建CustomData类 package com.ldb.android.e ...

  6. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- ApiWrapper

    前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝 ...

  7. Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- Messenger

    Messenger类实际是对Aidl方式的一层封装.本文只是对如何在Service中使用Messenger类实现与客户端的通信进行讲解,对Messenger的底层不做说明.阅读Android Prog ...

  8. 服务 AIDL 定向tag IPC Parcelable 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. Android IPC 结篇

    一.概述 Android 的 IPC 方式有 Bundle .共享文件.AIDL .Messenger .ContentProvider .Socket ,我们在实现进程间通信时要选择哪一种方式来实现 ...

随机推荐

  1. Usage of readonly and const

    Many new learners can not make sure the usage scenarios of readonly and const keywords. In my opinio ...

  2. ibatis 搭建总结

    一.搭建ibatis环境 1.导入ibatis的jar包,已及数据库驱动jar包ibatis-2.3.0.677.jar ibatis-dao-2.jar ibatis-sqlmap-2.jar ib ...

  3. linux查看cpu、内存信息

    #查看CPU信息(型号) cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c   # 总核数 = 物理CPU个数 X 每颗物理CPU的核数  # ...

  4. LVM 创建分区扩展分区记录

    LVM 原理   图片来自百度百科   测试环境centOS 7                 LVM version:     2.02.115(2)-RHEL7 (2015-01-28)     ...

  5. flex-mp3

    Mp3Player.as package ddw.adobe { import flash.events.Event; import flash.events.IOErrorEvent; import ...

  6. Codeforces Round #333 (Div. 1) B. Lipshitz Sequence 倍增 二分

    B. Lipshitz Sequence Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/601/ ...

  7. 通过ulimit改善linux系统性能(摘自IBM)

    本文介绍了 ulimit 内键指令的主要功能以及用于改善系统性能的 ulimit 用法.通过这篇文章,读者不仅能够了解 ulimit 所起的作用.而且能够学会怎样更好地通过 ulimit 限制资源的使 ...

  8. 【虚拟化实战】VM设计之一vCPU

    作者:范军 (Frank Fan) 新浪微博:@frankfan7 虚拟机需要多少个vCPU呢?是不是个数越多性能越好呢?这方面存在着很多误区.给VM配置CPU资源的时候,要精打细算才能最大可能的利用 ...

  9. SiteMesh学习笔记

    SiteMesh是一个轻量级的web应用框架,实现了Decorator模式.它的目标是将多个页面都将引用的jsp页面通过配置加载到相应的jsp文件中. 在我们的项目中,每个jsp都需要添加两个top和 ...

  10. 判断IE中iframe完美加载完毕的方法

    转: var iframe = document.createElement("iframe"); iframe.src = "http://www.planabc.ne ...