说起来AIDL,安卓开发者可能都听说过。但是真正使用过或者深入了解过的估计就不多了。本文将从一个例子出发,来简单说明AIDL的使用方法和原理。
AIDL是用来做进程间的通信的,有个最简单的需求。现在有两个项目,需要做通信调用。第一反应可能是写接口来做回调,但这个要求两个项目必须有依赖关系。假如现在我们要求不能让他们有任何的依赖关系又该如何处理呢,答案就是要做进程间的通信,使用AIDL。
通过上面的分析,我们也知道了,AIDL的使用是要求有两个不同的项目,其中一个项目来调用另外一个项目的方法。根据安卓Binder的规范,通过来说,调用方法的我们叫做客户端,被调用方法(即提供方法)的我们叫做服务端。
下面我们来直接上代码,先看服务端。
// Caculator.aidl
package com.example.server;
// Declare any non-default types here with import statements
interface Caculator {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int sum(int a, int b);
}
首先是AIDL文件,两边的必须保证完全一致,最好是复制粘贴。
编译后会产生一个对应的代理文件。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\IDE\\AndroidStudioProjects\\AIDL\\server\\src\\main\\aidl\\com\\example\\server\\Caculator.aidl
*/
package com.example.server;
// Declare any non-default types here with import statements
public interface Caculator extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.server.Caculator {
private static final java.lang.String DESCRIPTOR = "com.example.server.Caculator";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.server.Caculator interface,
* generating a proxy if needed.
*/
public static com.example.server.Caculator asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.server.Caculator))) {
return ((com.example.server.Caculator) iin);
}
return new com.example.server.Caculator.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_sum: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sum(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.server.Caculator {
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 int sum(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int sum(int a, int b) throws android.os.RemoteException;
}
我们先来查看这个文件的结构图。
首先它是一个继承android.os.IInterface的接口,然后接口中有咱们刚才自定义的方法,另外有一个抽象类Stub,是Binder的子类,然后实现了我们定义的接口。
然后是Stub类,Stub类里边有一个Proxy类。下边我们来一一分析他们的方法。
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
接口只是来定义规范的,所以在服务端我们要使用的其实是Stub类,然后在构造的时候需要传入一个Tag,DESCRIPTOR其实就是类名的全称,一个项目中是唯一的。
/**
* Cast an IBinder object into an com.example.server.Caculator interface,
* generating a proxy if needed.
*/
public static com.example.server.Caculator asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.server.Caculator))) {
return ((com.example.server.Caculator) iin);
}
return new com.example.server.Caculator.Stub.Proxy(obj);
}
服务端在生成这个Binder对象后,客户端可以使用asInterface把得到一个对应的接口实现类,使用到了DESCRIPTOR来区分,如果没有是new的一个Proxy对象。中间涉及到ServerManager和Binder的传输,有兴趣的同学做深入了解。
@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_sum: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sum(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact是真正来实现方法调用和数据的传输的,首先写入标记,然后把读取到的数据进行sum运算,然后再写回去。
最重要的来了,Proxy,刚才我们说过,返回给客户端的不是Stub,而是Proxy。
private static class Proxy implements com.example.server.Caculator {
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 int sum(int a, int b) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_sum, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
这里一定要注意Proxy的sum方法,大家要清楚,这个Proxy的代码是运行在客户端的,因为客户端使用asInterface转化后得到的就是Proxy。我们查看sum方法,它先把数据参数写入,然后进行传输,后边调用了 mRemote.transact,这个mRemote可以简单的认为就是Stub,然后内部走了Stub的onTransact,先读取数据,然后计算器后返回数据,中间使用的都是Binder机制的IPC通信,Proxy在接到数据后返回给调用者。
下边我们来上客户端和服务端的代码。
服务端
package com.example.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return CACULATOR;
}
private static final Caculator.Stub CACULATOR = new Caculator.Stub() {
@Override
public int sum(int a, int b) throws RemoteException {
return a + b;
}
};
}
客户端
package com.example.frank.myapplication;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Toast;
import com.example.client.Caculator;
public class MainActivity extends Activity {
private Caculator caculator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final int a = 1;
final int b = 1;
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.server", "com.example.server.MyService"));
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
caculator = Caculator.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
caculator = null;
}
}, Context.BIND_AUTO_CREATE);
try {
Toast.makeText(MainActivity.this, caculator.sum(a, b) + "", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
try {
Toast.makeText(MainActivity.this, caculator.sum(a, b) + "", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}, 5000);
}
}
两点需要注意的:
1,binderService是异步方法,所以尽量早的调用,并且在使用的时候要注意非空判断。
2,新版本的启动服务的方法不支持隐式调用,注意写法。
好了,对AIDL的简单使用相信大家都有一定的了解了,但是Binder机制的IPC通信内容非常多,希望后续能够再来分享。
网友评论