Android进程间通信--Binder,AIDL,Parcelable

1.  

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类型的对象传递到服务端,实现复杂类型的通信传递。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值