基于AIDL结合localSocket的跨进程在android上的运用
Posted Jarlene
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于AIDL结合localSocket的跨进程在android上的运用相关的知识,希望对你有一定的参考价值。
在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链接讲述完毕。
以上是关于基于AIDL结合localSocket的跨进程在android上的运用的主要内容,如果未能解决你的问题,请参考以下文章