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

Android中的Binder(二)

2016-05-06 23:30 525 查看
前言:回顾一下上一篇博文,上一篇说到,Binder是Android系统中IPC机制的底层依赖,描绘了Binder这种通信架构的基本原理图,并且谈到了使用Binder时需要解决的两个问题。即:1.客户端如何获取Binder对象的引用。2.如何协商服务端的函数标识和参数放入包裹的顺序问题。以便于客户端调用服务时,服务端能识别并提供正确的服务。通过上一篇的介绍,相信大家已经知道了客户端通过Service来获取Binder引用的方式,并且也了解了通过重写服务端Binder的onTransact()方法和客户端Binder的transact()方法来解决上述的第二个问题。对于有经验的程序员,完全可以通过这两种做法来实现IPC通信。于此同时,Google的大神们也为开发者提供了更为方便的AIDL工具,这个工具可以帮我们解决第二个问题,这样,开发者就可以完全专注于业务本身。俗话说:“不做重复的轮子”,既然有这种工具,何乐而不用呢?接下来让我们来走进AIDL。之所以将AIDL放在Binder这里讲,是因为这里着重分析AIDL如何解决Binder通信中的第二个问题。若是这里感觉有点迷糊的话,可以看看上一篇博文《Android中的Binder(一)》

一、什么是AIDL?

AIDL,全称为Android Interface Definition Language,即android接口定义语言。顾名思义,AIDL只是一种定义语言,用它的规范定义的接口,可以被aidl工具识别并生成对应的类。(aidl工具在sdk中,可以自行查看)。相信这样比较抽象,接下来我们通过一个小小的例子来看看aidl如何工作。

二、一个简单的AIDL通信示例

服务端代码,注意与上一篇中的服务端代码进行比较,区别已经在注释中体现出来了。

public class ServerService extends Service {

/**
* 创建服务端binder对象(不用aidl时需要自定义Binder子类,并在Service中返回)
* 并且我们不用在服务端自行书写onTransact()方法
*/
private Binder serverBinder = new IMyServerService.Stub() {
@Override
public void service2(String args) throws RemoteException {
System.out.println("---service2---:" + args);
}

@Override
public void service1() throws RemoteException {
System.out.println("---service1---");
}
};

@Override
public void onCreate() {
Log.i("DEBUG", "---onCreate()---");
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
Log.i("DEBUG", "---onBind()---");
return serverBinder;
}

@Override
public boolean onUnbind(Intent intent) {
Log.i("DEBUG", "---onUnbind()---");
return super.onUnbind(intent);
}

}


aidl接口定义,新建com.example.aidl包,新建IMyServerService接口(自行命名),点击保存,可以在gen目录下找到eclipse帮我们自动生成的类IMyServerService.java。之所以新建包,是因为这样等会可以直接将整个包拷贝到客户端中。

package com.example.aidl;

interface IMyServerService{
void service1();
void service2(String args);
}


客户端代码,同样注意与上一篇的比较,同样体现在注释中。

public class ClientActivity extends Activity {

private Button btn_connectService;
private Button btn_disconnectService;
private Button btn_getService1;
private Button btn_getService2;
// 服务端Service的action,根据自己而变
private Intent intent = new Intent("com.wwt.server.ServerService");
private IMyServerService mService;

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 绑定服务端
btn_connectService = (Button) findViewById(R.id.btn_connectService);
btn_connectService.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});

// 取消绑定服务
btn_disconnectService = (Button) findViewById(R.id.btn_disconnectService);
btn_disconnectService.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
}
});

// 获取服务端服务1。在没有aidl的时候,客户端需要编写transact()方法,
// 反之则不用。aidl生成的类自动为我们屏蔽了这一细节,即为我们实现了transact()函数。
btn_getService1 = (Button) findViewById(R.id.btn_getService1);
btn_getService1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
mService.service1();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});

// 获取服务端服务2
btn_getService2 = (Button) findViewById(R.id.btn_getService2);
btn_getService2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
mService.service2("Hello");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}

/**
* 通过回调获取Binder对象(可能是服务端的,也可能是Binder驱动中的Binder对象, 取决于本地或是远程调用。)
*/
private ServiceConnection connection = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {

}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// IMyServerService的内部类Stub的asInterface()方法会根据客户端获取到的
// Binder对象的来源来决定调用客户端服务的方式,即是否采用代理。
mService =IMyServerService.Stub.asInterface(service);
}
};
}


服务端不需要界面直接运行,客户端界面比较简单就不写出来了。

运行结果如下:



工作是正常的。

三、AIDL如何解决前言中提到的Binder通信的第二个问题

打开gen目录,找到aidl工具为我们生成的类,我们发现这个类名字和我们定义的接口名称一致,并且继承了IInterface接口。接下来我们只看关键的代码。

public static abstract class Stub extends android.os.Binder implements
com.example.aidl.IMyServerService {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyServerService";

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

/**
* Cast an IBinder object into an com.example.aidl.IMyServerService
* interface, generating a proxy if needed.
*/
public static com.example.aidl.IMyServerService asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.IMyServerService))) {
return ((com.example.aidl.IMyServerService) iin);
}
return new com.example.aidl.IMyServerService.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_service1: {
data.enforceInterface(DESCRIPTOR);
this.service1();
reply.writeNoException();
return true;
}
case TRANSACTION_service2: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.service2(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.example.aidl.IMyServerService {
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 void service1() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_service1, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}

@Override
public void service2(java.lang.String args)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(args);
mRemote.transact(Stub.TRANSACTION_service2, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}

static final int TRANSACTION_service1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_service2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
需要注意的有以下这些点:

我们看到Stub类继承自Binder类,因此其本质也是一个Binder类,这也是我们能将其当成服务端Binder返回的原因。其次Stub类实现了我们在aidl文件中定义的接口,但是它本身是一个抽象类,并未为我们提供接口方法的实现,因此我们需要在服务端新建的Binder(Stub)对象的时候提供接口方法的具体实现,这便是该服务端提供的服务。
接着我们看到Stub类中“最重要”的一个方法asInterface()。不难发现,这个方法在客户端被调用,客户端同时会将获取到的Binder对象传入这个方法中。因此在asInterface()中会进行一次判断,若是发现从客户端传过来的Binder对象是服务端的(queryLocalInterface()方法),则直接向上转型为IMyServerService类型并返回。若是发现从客户端传过来的Binder对象是Binder驱动中的,那么就需要用到Stub的Proxy代理子类了。
仔细看Proxy代理类,它在asInterface()方法判断为远程调用时被实例化并返回给客户端。那么为什么需要代理类呢?为什么不能像本地Binder那样直接向上转型为IMyServerService呢?其实这样是行不通的,因为就像上篇说的,远程调用客户端获得的Binder对象是Binder驱动中的,需要注意的是,虽然服务端的Binder(Stub)对象实现了IMyServerService接口,可以正确转型,但是Binder驱动中的Binder对象并不能实现这种转型!因此我们需要代理,让它为我们操纵这个Binder对象,调用其transact()方法。
到这里相信很多人已经发现了,aidl工具为我们生成的Stub类里面重载了本该在服务端实现的onTransact()方法和本该在客户端实现的transact()方法,这两个方法就是决定方法调用的。因此我们说aidl为我们统一了放入参数的顺序和函数标识的问题!

可能大家对于上述的第三点胡有点疑虑,即代理类真的有必要存在吗的问题,下面进行简单的验证。将CilentActivity作一点小小的修改,改动如下,直接将客户端获取到的Binder对象进行转型。

<span style="font-size:14px;">mService =IMyServerService.Stub.asInterface(service);</span>
改为:

mService =(IMyServerService) service
运行结果如下:(类型转换错误,得证)





四、总结

看到这里,相信大家对于Binder有了比较深的理解了,对AIDL也有了比较浅的认识。利用AIDL可以实现的功能远不及此,有机会下次在讨论。同时对于Binder通信机制来说,AIDL也不是必须的,我们不仅可以像上一篇说的那样,自己重写onTransact()和transact()方法来实现,甚至你可以完全不需要aidl文件,直接写出一个类似于IMyServerService的java类(aidl工具只是为我们生成这个类而已)。
最后在附上aidl中支持的类型:

基本数据类型
String和CharSequence
List(元素需要时aidl支持的)
Map(元素需要是aidl支持的)
AIDL
Parcelable
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: