在android上有很多跨进程的通讯方法例如aidl,messenger,ContentProvider,BroadCast,Socket等等,想要了解这些IPC机制具体可以参考这篇文章,这是一个序列,他讲述了androd中的跨进程方法。但是本文讲述的是和其描述的一些不同的方法,我们讨论的是基于aidl结合localSocket方案。
AIDL简介
AIDL:Android Interface Definition Language,即Android接口定义语言;Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。我们知道建立AIDL服务的步骤有一下三个步骤:
建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同
建立一个服务类(Service的子类)
实现由aidl文件生成的Java接口
网上有很大aidl的例子,这里就不给出,下面我们讲述如何使用aidl和localSocket进行ipc。
AIDL和LocalSocket建立联系
第一步:新建SocketBindClient和SocketBindServer类,该类继承了接口IBinder接口,其代码如下:
public class SocketBindClient extends BaseSocketBind implements IBinder {
private final static String TAG = "SocketBindClient";
private final static int CONNECTION_TIMEOUT_MILLIS = 5000;
private String mSocketName = null;
public SocketBindClient(String socketName){
mSocketName = socketName;
}
@Override
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Parcel par = Parcel.obtain();
data.setDataPosition(0);
par.writeInt(code);
byte[] tmp = data.marshall();
par.writeByteArray(tmp);
par.setDataPosition(0);
byte[] arbuf = par.marshall();
byte[] bufIn = new byte[arbuf.length];
System.arraycopy(arbuf, 0, bufIn, 0, arbuf.length);
// socket data
byte[] bufOut = TransferBufferClient(bufIn, mSocketName);
if (null != bufOut) {
reply.setDataPosition(0);
reply.unmarshall(bufOut, 0, bufOut.length);
reply.setDataPosition(0);
}
par.recycle();
return false;
}
.... // 忽略不重要的代码
}
这个类主要是实现了IBinder接口的transact方法,在这个方法中调用了TransferBufferClient方法,这个是基类BaseSocketBind中的一个方法。具体如下:
public byte[] TransferBufferClient(byte[] buffer, String mSocketName) {
LocalSocket socket = new LocalSocket();
byte[] outBuf = null;
OutputStream os = null;
InputStream is = null;
byte[] headerByte = Constance.getSocketHeader();
try {
socket.connect(new LocalSocketAddress(mSocketName));
if (socket.isConnected()) {
socket.setReceiveBufferSize(32 * 1024);
socket.setSendBufferSize(32*1024);
os = socket.getOutputStream();
if (null != os) {
byte[] bufLen = UnitConvert.int2bytes(buffer.length, ByteOrder.BIG_ENDIAN);
byte[] version = UnitConvert.int2bytes(Constance.version, ByteOrder.BIG_ENDIAN);
os.write(headerByte);
os.write(version);
os.write(bufLen);
os.write(buffer);
os.flush();
}
}
is = socket.getInputStream();
if (null != is) {
byte[] checkHeader = new byte[10];
is.read(checkHeader);
if (checkHeader == Constance.getSocketHeader()) {
byte[] version = new byte[4];
is.read(version);
int nServerVer = UnitConvert.bytes2int(version, ByteOrder.BIG_ENDIAN);
byte[] res = new byte[4];
is.read(res);
int nsize = UnitConvert.bytes2int(res, ByteOrder.BIG_ENDIAN);
if (nsize > socket.getSendBufferSize()) {
} else if (nServerVer > Constance.version) {
} else if (nsize > 0){
outBuf = new byte[nsize];
is.read(outBuf);
}
}
}
if (null != is) {
is.close();
}
if (null != os) {
os.close();
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != is) {
is.close();
}
if (null != os) {
os.close();
}
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return outBuf;
}
在这个方法中,新建一个localSocket,通过这个Socket来获取传输过来的数据,这个数据是具有一定格式的。(当然格式是自己定义的)
/*
* 协议格式
*
* -------
* |8字节 | 固定为BAIDUMUS
* |4字节 | 协议版本
* |4字节 | 数据内容长度
* |..... | 数据内容
*
*/
根据这个协议格式获取传输过来的内容。同理,我们还定义了一个Server端。具体代码如下:
public class SocketBindServer extends BaseSocketBind implements IBinder {
private final static String TAG = "SocketBindServer";
@Override
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Parcel par = Parcel.obtain();
data.setDataPosition(0);
par.writeInt(code);
par.writeByteArray(data.marshall());
par.setDataPosition(0);
byte[] arbuf = par.marshall();
byte[] bufIn = new byte[arbuf.length];
System.arraycopy(arbuf, 0, bufIn, 0, arbuf.length);
// socket data
byte[] bufOut = TransferBufferClient(bufIn, Constance.SOCKETNAME);
reply.setDataPosition(0);
reply.unmarshall(bufOut, 0, bufOut.length);
reply.setDataPosition(0);
par.recycle();
return true;
}
.... // 忽略不重要的代码
}
我们把Server端和Client端都弄好了,现在要做的就是怎么让其通讯,看如下代码:
public class SocketListenThread extends Thread {
@Override
public void run() {
while (true) {
try {
LocalSocket socket = mServerSocket.accept();
if (mbStoped){
if (null != socket){
try{
OutputLog("mServerSocket.accept is Stop\n");
socket.close();
Log.e(TAG, "mServerSocket.accept is Stop");
} catch (IOException e) {
e.printStackTrace();
}
}
break;
}
if (null != socket) {
startEchoThread(socket);
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
}
}
}
我们弄了一个线程来让LocalServerSocket接受LocalSocket,这个线程一直在运行,他的作用就是一直监听是否有LocalSocket请求connect,而真正进行数据处理是放在一个线程池中:也就是上面的startEchoThread方法,其代码如下:
private void startEchoThread(final LocalSocket socket) {
IPCRunnable task = new IPCRunnable(socket);
if(false == mThreadPool.isShutdown()) {
try {
mThreadPool.execute(task);
} catch (NullPointerException e) {
e.printStackTrace();
} catch (RejectedExecutionException e) {
e.printStackTrace();
}
}
}
IPCRunnable就是真正将LocalServerSocket和LocalSocket数据通讯链接起来的,其代码如下:
class IPCRunnable implements Runnable {
private LocalSocket mSocket = null;
public IPCRunnable(LocalSocket socket) {
mSocket = socket;
}
@Override
public void run() {
InputStream is = null;
OutputStream os = null;
try {
mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
int SendBufferSize = mSocket.getSendBufferSize();
int ReceiveBufferSize = mSocket.getReceiveBufferSize();
is = mSocket.getInputStream();
os = mSocket.getOutputStream();
// 读取头部,检查协议是否一致。
is.read(checkHeader);
byte[] ver = new byte[4];
is.read(ver);
int clientver = UnitConvert.bytes2int(ver, ByteOrder.BIG_ENDIAN);
if (clientver > BindSocketName.PROTOCAL_VER) {
return;
}
byte[] res = new byte[4];
is.read(res);
int ret = UnitConvert.bytes2int(res, ByteOrder.BIG_ENDIAN);
if (ret > ReceiveBufferSize) {
return;
}
byte[] values = new byte[ret];
is.read(values);
//读取数据内容
byte[] retValue = TransferBufferServer(values);
if (null != retValue) {
byte[] prover = UnitConvert.int2bytes(BindSocketName.PROTOCAL_VER, ByteOrder.BIG_ENDIAN);
os.write(fixHeaderBytes);
os.write(prover);
os.write(UnitConvert.int2bytes(retValue.length, ByteOrder.BIG_ENDIAN));
os.write(retValue);
os.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里最主要的方法是TransferBufferServer,他的作用是将aidl的序列化数据通过IBinder的onTransact方法,而这个这个binder其实就是LocalBinderClient
public byte[] TransferBufferServer(byte[] buffer) {
byte[] outBuffer = null;
Parcel argvs = Parcel.obtain();
Parcel reply = Parcel.obtain();
argvs.unmarshall(buffer, 0, buffer.length);
argvs.setDataPosition(0);
int code = argvs.readInt();
byte[] bData = argvs.createByteArray();
Parcel dataArgv = Parcel.obtain();
dataArgv.unmarshall(bData, 0, bData.length);
try {
dataArgv.setDataPosition(0);
String desc = dataArgv.readString();
BinderWrapper binder = null;
IInterface service = ServiceManager.getInstance().getService(desc);
binder = (BinderWrapper) service.asBinder();
boolean bRetCode = binder.onTransact(code, dataArgv, reply, 0);
if (bRetCode) {
reply.setDataPosition(0);
byte[] buf = reply.marshall();
if (0 != buf.length) {
outBuffer = new byte[buf.length];
System.arraycopy(buf, 0, outBuffer, 0, buf.length);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
dataArgv.recycle();
argvs.recycle();
reply.recycle();
}
return outBuffer;
}
到这里我们就将LocalSocket和LocalServerSocket通讯链接上来,接下来就是实现和让让aidl绑定到LocalBinderClient上、其实这个很简单,我们都知道,在aidl的Stub.Proxy类是AIDL代理类,而这个代理类的构造方法有一个参数是IBinder
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
而Stub.asInterface返回的就是Proxy类。继而构造Proxy的时候,将LocalBinderClient作为参数将Proxy构造出来,就可以了。
final Object obj = proxyClass.getConstructor(IBinder.class).newInstance(mSocketBinder);
mSocketBinder其实就是LocalBinderClient。自此我们就将aidl和LocalSocket链接讲述完毕。