基于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链接讲述完毕。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值