Android进程间通信(IPC)机制---Binder
摘自: http://blog.csdn.net/luoshengyang/article/details/6618363
在Android系统中,每一个应用程序都是由一些Activity和Service组成的,这些Activity和Service有可能运行在同一个进程中,也有可能运行在不同的进程中。那么,不在同一个进程的Activity或者Service是如何通信的呢?这就是本文中要介绍的Binder进程间通信机制了。
我们知道,Android系统是基于Linux内核的,而Linux内核继承和兼容了丰富的Unix系统进程间通信(IPC)机制。有传统的管道(Pipe)、信号(Signal)和跟踪(Trace),这三项通信手段只能用于父进程与子进程之间,或者兄弟进程之间;后来又增加了命令管道(Named Pipe),使得进程间通信不再局限于父子进程或者兄弟进程之间;为了更好地支持商业应用中的事务处理,在AT&T的Unix系统V中,又增加了三种称为“System V IPC”的进程间通信机制,分别是消息队列(Message)、共享内存(Share Memory)和信号量(Semaphore);后来BSD Unix对“System V IPC”机制进行了重要的扩充,提供了一种称为插口(Socket)的进程间通信机制。若想进一步详细了解这些进程间通信机制,建议参考 Android学习启动篇 一文中提到《Linux内核源代码情景分析》一书。
但是,Android系统没有采用上述提到的各种进程间通信机制,而是采用Binder机制,难道是因为考虑到了移动设备硬件性能较差、内存较低的特点?不得而知。Binder是一种进程间通信机制,提供远程过程调用(RPC)功能。在Android系统的Binder机制中,由一系统组件组成,分别是Client、Server、Service Manager和Binder驱动程序,关系如图所示。其中Client、Server和Service Manager运行在用户空间,Binder驱动程序运行内核空间。Binder就是一种把这四个组件粘合在一起的粘结剂了,其中,核心组件便是Binder驱动程序了,Service Manager提供了辅助管理的功能,Client和Server正是在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。Service Manager和Binder驱动已经在Android平台中实现好,开发者只要按照规范实现自己的Client和Server组件就可以了。
1. Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中;
2. Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Server;
3. Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信;
4. Client和Server之间的进程间通信通过Binder驱动程序间接实现;
5. Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力;
另一篇文章:http://www.linuxidc.com/Linux/2012-07/66195.htm
1.binder用来做什么?用来实现不同进程之间的通信。
2.Binder是什么?binder属于一个驱动,工作在linux层面,运行在内核态,它的操作完成是基于一段内存。所以我们开发的程序中对binder的使用都是通过系统的调用来完成的。
3.binder是怎样实现进程通信的?我们来通过Binder的架构来了解它实现进程间通信(IPC)的过程。
Binder架构由服务端,binder驱动,客户端三个部分构成,其中服务端,客户端处在用户空间,而binder驱动处在内核空间。
服务器端:一个Binder服务器端就是一个Binder类的对象。当创建一个Binder对象后,内部就会开启一个线程,这个线程用于接收binder驱动发送的信息,收到消息后,会执行相关的服务代码。
Binder驱动:当服务器端成功创建一个Binder对象后,Binder驱动也会相应创建一个mRemote对象,该对象的类型也是Binder类。客户端就可以借助这个mRemote对象来访问远程服务器。
客户端:客户端要想访问Binder的远程服务,就必须获取远程服务的Binder对象在binder驱动层对应的mRemote引用。当获取到mRemote对象的引用后,就可以调用相应Binder对象的服务了。
在这里,我们可以看到,客户端是通过Binder驱动来调用服务端的相关服务。首先,在服务端创建一个Binder对象,然后相应在Binder驱动中创建一个Binder对象,接着客户端通过获取Binder驱动中Binder对象的引用来调用服务端的服务。在Binder机制中正是借着Binder驱动将不同进程间的组件bind(粘连)在一起,实现通信。
基础知识
要为 service 提供绑定,你必须实现 onBind()回调方法 .此方法返回一个 IBinder 对象,此对象定义了客户端可以用来与 service 交互的程序接口.一个客户端可以通过调用 bindService() 绑定到 service .当它这样做时,它必须提供了一个 ServiceConnection 的实现.这个实现用于监视客户端与 service 的连接. bindService() 方法会立即返回并且不会返回任何值,但当 Android 系统创建客户端与 service 之间的连接时,它调用 ServiceConnection 的 onServiceConnected() 来传送客户端用来与 servcie 通信的 IBinder .
多个客户端可以同时连接到一个 service .然而,系统只在第一个客户端绑定时才调用你的 service 的 onBind() 方法来接收 IBinder .之后系统把同一个 IBinder 传给其它客户端,所以不会再调用 onBind() .
2. AIDL
服务(Service)是Android系统中4个应用程序组件之一。服务主要用于两个目的:后台运行和跨进程访问。通过启动一个服务,可以在不显示界面的前提下在后台运行指定的任务,这样可以不影响用户做其他事情。通过AIDL服务可以实现不同进程之间的通信,这也是服务的重要用途之一。
跨进程访问(AIDL服务)
Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。Activity和Broadcast都可以跨进程通信,除此之外,Content Provider也可以进行跨进程通信。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口,称为AIDL(Android Interface Definition Language)服务。
建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:
(1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。详细介绍见后面实例的内容。
(2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
(3)建立一个服务类(Service的子类)。
(4)实现由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
首先要了解一下AIDL对Java类型的支持:
1.AIDL支持Java原始数据类型。
2.AIDL支持String和CharSequence。
3.AIDL支持传递其他AIDL接口,但你引用的每个AIDL接口都需要一个import语句,即使位于同一个包中。
4.AIDL支持传递实现了android.os.Parcelable接口的复杂类型,同样在引用这些类型时也需要import语句。(Parcelable接口告诉Android运行时在封送(marshalling)和解封送(unmarshalling)过程中实现如何序列化和反序列化对象,你可以很容易联想到java.io.Serializable接口。有些朋友可能会有疑问,两种接口功能确实类似,但为什么Android不用内置的Java序列化机制,而偏偏要搞一套新东西呢?这是因为Android团队认为Java中的序列化太慢,难以满足Android的进程间通信需求,所以他们构建了Parcelable解决方案。Parcelable要求显示序列化类的成员,但最终序列化对象的速度将快很多。另外要注意的是,Android提供了两种机制来将数据传递给另一个进程,第一种是使用Intent将数据束(Bundle)传递给Activity,第二种也就是Parcelable传递给Service。这两种机制不可互换,不要混淆。也就是说,Parcelable无法传递给Activity,只能用作AIDL定义的一部分)。
5.AIDL支持java.util.List和java.util.Map,但是有一些限制。集合中项的允许数据类型包括Java原始类型、String、CharSequence或是android.os.Parcelable。无需为List和Map提供import语句,但需要为Parcelable提供import语句。
6.非原始类型中,除了String和CharSequence以外,其余均需要一个方向指示符。方向指示符包括in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。
3.
使用AIDL实现进程间的通信之复杂对象传递
摘自: http://blog.csdn.net/liuhe688/article/details/6409708
也是要有服务端和客户端,其结构分别为:
1)服务端:
其中,Person类是我们要在服务端和客户端中间进行传递的类型,Person.java代码如下:
package com.example.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private int sex;
// 必须提供一个名为CREATOR的static final属性 ,该属性需要实现android.os.Parcelable.Creator<T>接口
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
public Person createFromParcel(Parcel source) {
return new Person(source);
}
public Person[] newArray(int size) {
return new Person[size];
}
};
public Person() {
}
public Person(Parcel source) {
readFromParcel(source);
}
// 注意读取变量和写入变量的顺序应该一致 不然得不到正确的结果
private void readFromParcel(Parcel source) {
name = source.readString();
sex = source.readInt();
}
public int describeContents() {
return 0;
}
// 注意写入变量和读取变量的顺序应该一致 不然得不到正确的结果
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(sex);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
然后我们需要在同一包下建立一个与包含复杂类型的Person.java文件匹配的Person.aidl文件,代码如下:
package com.example.aidl;
parcelable Person; //parcelable是小写的。
这个Person.aidl文件很简单,就是定义了一个Parcelable类,告诉系统我们需要序列化和反序列化的类型。每一个实现了Parcelable的类型都需要对应的.aidl文件。AIDL编译器在编译AIDL文件时会自动查找此类文件。
接下来,我们需要创建一个DataService.aidl文件,以接收类型为Person的输入参数,以便客户端可以将Person传递给服务。
package com.example.aidl;
import com.example.aidl.Person;
interface DataService{
String greet(in Person person);
}
此时,在eclipse插件的帮助下,AIDL编译器会自动编译生成一个DataService.java文件,存放于/gen/相应包下。
接下来,就该完成我们的AIDLService逻辑部分了,AIDLService.java代码如下:
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
public IBinder onBind(Intent intent) {
return stub;
}
public void onDestroy() {
super.onDestroy();
}
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
// 定义提供给Client端调用的方法
DataService.Stub stub = new DataService.Stub() {
@Override
public String greet(Person person) throws RemoteException {
Log.i(TAG, "greet(Person person) called");
String greeting = "hello, " + person.getName();
switch (person.getSex()) {
case 0:
greeting = greeting + ", you're handsome.";
break;
case 1:
greeting = greeting + ", you're beautiful.";
break;
}
return greeting;
}
};
}
最后,在AndroidManifest.xml中配置该服务:
<service android:name=".AIDLService">
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
服务端就已经完成了。
2)客户端
我们需要把服务端的Person.java、Person.aidl和IGreetService.aidl拷到对应的包下。我们主要来看看MainActivity.java代码:
public class MainActivity extends Activity {
private Button button, button2;
private DataService dataService;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button1);
button2 = (Button) this.findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.AIDLService");
boolean flag=bindService(intent, connection, BIND_AUTO_CREATE);
if(flag){
Log.i("dddd", "--->bind success!");
}else{
Log.i("dddd", "--->bind fail!");
}
}
});
button2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
Person person=new Person();
person.setName("shopping");
person.setSex(0);
String ret=dataService.greet(person);
Toast.makeText(MainActivity.this, ret, 1).show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private ServiceConnection connection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
Log.i("ServiceConnection", "onServiceDisconnected() called");
}
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("ServiceConnection", "onServiceConnected() called");
dataService = DataService.Stub.asInterface(service);
}
};
}
通过这种方式,就可以将Person类型的对象传递到服务端,实现复杂类型的通信传递。