您的位置:首页 > 移动开发 > Android开发

Android开发艺术探索学习-IPC之Binder(一)

2015-11-18 19:19 549 查看
1. Binder简介

1.1 What is Binder?

Android Dev Doc:Base class for a remotable object, the core part of a lightweight remote procedure
call mechanism defined by
IBinder
.
This class is an implementation of IBinder that provides standard local implementation of such an object.

Binder是一个远程对象的基类,是一个轻量级远程通信机制的核心部分,通过IBinder定义。Binder类实现IBinder接口,并通过提供标准的本地接口对象实现IBinder。

我们以前也接触过一些IPC方式,比如:管道(pipe)、有名管道(FIFO)、信号(signal)、消息队列、共享内存、套接字(socket)等,但是Android系统并没有采取这些方式,而是使用Binder完成IPC。

1.2 why is Binder?

由于移动设备的特殊性,在IPC要尽量满足两个方面的要求:安全性高、传输性能高。

首先从传输性能上看:

socket传输效率低,开销大,因此主要还是用于跨网络的进程间通信。消息队列和管道采用的是存储转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。

共享内存的方式虽然不用数据拷贝,但是控制使用起来很麻烦,大家如果有兴趣的话可以看下Android的匿名共享内存方法,其实是很复杂的,罗升阳的Android系统匿名共享内存Ashmem(Anonymous
Shared Memory)简要介绍和学习计划
中有详细介绍。

表1 各种IPC方式数据拷贝次数

IPC
数据拷贝次数
共享内存
0
Binder
1
Socket/管道/消息队列
2
其次从安全性上看:

传统的IPC其实是没有任何安全措施,完全依赖上层协议来确保,其接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份。但是Binder会为发送方添加UID/PID身份(由系统添加),安全性高。
1.3 Binder的注意事项
有一点我们需要牢记在心:当我们的进程被“Kill”,那么就需要在该进程重启时重新创建一个Binder对象并[b]re-attach到该进程。例如,当你如果在Activity里面使用Binder,如果你的Activity未启动,那么它就随意可能会系统回收。当Activity重建时,你需要重建一个新的Binder对象并放在正确的地方。当然你也需要注意你的进程因其他原因而启动(比如收到广播),那么这种情况activity不会重建也就不会跑到创建Binder对象的代码了。[/b]
1.4 AIDL
如果大家有兴趣阅读Android源码的话,就会发现Android FrameWork层存在着很多Binder机制。因此理解Binder的工作原理是很重要的。为了能让咱们程序员能很简单地使用Binder,Android为我们提供了一个非常简单地使用Binder的工具,AIDL。那么如何使用AIDL呢?请参考Android官方文档:http://developer.android.com/guide/components/aidl.html
这里主要分析下,AIDL文件自动生成的JAVA文件。aidl文件名为:IAidlCall.aidl,代码如下。
package com.example.aidldemo;
interface IAidlCall{
String getName();
}

自动生成的IAidlCall.java,代码如下。

/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: /Users/chenjiawei/Documents/AndroidSpace/ClientDemo/src/com/example/aidldemo/IAidlCall.aidl
*/
package com.example.aidldemo;

public interface IAidlCall extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.example.aidldemo.IAidlCall {
// Binder的唯一标示,一般用当前Binder的类名来标示
private static final java.lang.String DESCRIPTOR = "com.example.aidldemo.IAidlCall";

/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.example.aidldemo.IAidlCall
* interface, generating a proxy if needed.
*/
public static com.example.aidldemo.IAidlCall asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidldemo.IAidlCall))) {
return ((com.example.aidldemo.IAidlCall) iin);
}
return new com.example.aidldemo.IAidlCall.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.example.aidldemo.IAidlCall {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public java.lang.String getName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public java.lang.String getName() throws android.os.RemoteException;
}


IAidlCall是一个接口,其继承于android.os.IInterface。所以AIDL自动生成的java文件都要继承该接口,该接口代码如下。

/**
* Base class for Binder interfaces.  When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}

IAidlCall接口中声明了一个静态内部抽象类Stub,该类继承Binder类并实现IAidlCall接口。因此Stub其实就是一个Binder类。
在我们实际的使用AIDL过程中,客户端和服务端都要与Stub打交道。在服务端中,我们需要实现Stub类,代码如下。
public class MyServiceImpl extends IAidlCall.Stub {

@Override
public String getName() throws RemoteException {

return "Jack";
}

}

在客户端中,我们需要调用Stub类中的asInterface方法来得到IAidlCall对象,代码如下。
private ServiceConnection conn = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
call = IAidlCall.Stub.asInterface(service);
try {
Toast.makeText(getApplicationContext(), call.getName(),
Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};

接着我们看下asInterface方法中的代码,看完该方法我们就可以了解IAidlCall.java工作流程。

/**
* Cast an IBinder object into an com.example.aidldemo.IAidlCall

* interface, generating a proxy if needed.

*/

public static com.example.aidldemo.IAidlCall asInterface(android.os.IBinder obj) {

//首先判断IBinder是否为空,因为Binder可以“死亡”,前面的有提过
if ((obj == null)) {
return null;
}
//这里queryLocalInterface方法得到IInterface对象,这是通过attachInterface赋值过去的
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//如果iin不为空并且是IAidlCall实例则将其强转成IAidlCall对象并返回
if (((iin != null) && (iin instanceof com.example.aidldemo.IAidlCall))){
return ((com.example.aidldemo.IAidlCall) iin);
}
//如果lin为空或者不是IAidlCall实例,则返回Proxy对象
return new com.example.aidldemo.IAidlCall.Stub.Proxy(obj);
}
这里我们可以看到一个Proxy类,该类是Stub里的静态类且继承于IAidlCall,在Proxy类中有真正完成getName()方法实现的过程。实现getName方法的代码如下。

@Override
public java.lang.String getName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

这里需要注意的是_data、_reply两个Parcel对象和transact方法。
_data是入参序列,如果调用的远程方法需要入参就通过_data的writeInt、writeLong等方法将参数写入_data中。
_reply是方法的返回值序列,如果调用的远程方法有返回值,就通过_reply的readInt、readLong等方法将参数读到_reply中。
这里最主要的方法是transact(intcode,
Parceldata, Parcelreply, intflags)方法。
1)第一个参数code代表的是方法的代码,例如getName()的方法code为,
static final int TRANSACTION_getName
= (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
知道code就能知道调用的是哪个方法。
2)第二个参数是指远程方法的入参。
3)第三个参数是指远程方法的返回值。
4)第四个参数是指额外操作标志,0代表的是普通的RPC,其他值代表的是one-way RPC。
因此我们,可以通过模仿写出transact方法所需要的参数,通过RemoteObject(Binder)调用其transact方法就可以完成远程方法的调用,最后会给出相应地例子。
Binder中transact方法实现代码如下。

/**
* Default implementation rewinds the parcels and calls onTransact.  On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
从该代码中我们可以看到调用了onTransact方法,在该方法中会把远程方法返回值写入到_reply中。在IAidlCall接口中的Stub类中,我们可以看到onTransact方法的实现,代码如下。

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
// 这里将getName的返回值写入到reply中
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
至此,我们分析完了IAidlCall接口类及其工作过程。其实我们也可以完全自己动手写出IAidlCall.java,在《Android开发艺术探索》书中就有给出相应地例子。下面可以看看AIDL的工作图。



这里有两点需要注意:
首先,当客户端发起远程请求的时候,由于当前进程会被挂起直到服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求;其次,由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。
通过以上介绍,可以大致了解了Binder的工作方式,后续还将继续深入了解,当然也不会太深,都是结合《Android开发艺术探索》和工作上所碰到的问题进行概述。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: