基于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上的运用的主要内容,如果未能解决你的问题,请参考以下文章

基于binder的跨进程通讯之使用AIDL实现Demo

Binder的使用(跨进程——AIDL,非跨进程)

Android 跨进程通信-从源码分析AIDL跨进程通信实现

Android跨进程通信——AIDL原理解析

使用AIDL跨进程通信

朝花夕拾跨进程通信,你只知道AIDL,就OUT了