Android中的进程之间的通信

Android中的进程通信主要有以下种

1.广播

广播是比较常用的进程间通信的方式,而且实现起来也比较简单
缺点:某些手机对于静态注册的广播的发送和接收延迟非常严重,例如一些配置很低的手机,华硕,联想的部分低配手机就对静态注册的广播有较长的延迟,我曾经遇到过的有延迟1到2分钟的;广播的数据传输是有限的,如果传输的数据量太大会报错

2.AIDL

AIDL是用在Service和外部进程之间的通信,如果是Service单独进程和App主进程进行通信的话可以考虑采用AIDL来实现;
第一步:定义AIDL接口

package com.xtc.sync;

import com.xtc.sync.ICloseCallback;
// Declare any non-default types here with import statements

interface IConnectionService {

    /**
    * 连接IM服务器
    */
    int connect(String hostname, int port);

    /**
    * 发送数据
    */
    int send(in byte[] data);

    /**
    * 心跳包
    */
    void heartbeat();

    /**
    * 关闭TCP连接并停止服务
    */
    void close();

    void closeForBack(ICloseCallback icloseCallback);

    /**
    *连接是否关闭
    */
    boolean isClose();

    /**
    *把服务器ip和端口信息传进来
    */
    void initServerInfo(in String[] serverInfo);

    /**
    *切换连接主机
    */
    void switchHost();

}

有些方法参数前面有“in”修饰,例如send(in byte[] data),这便是是外部调用send()接口,然后传入data的字节数组,加入是想从AIDL绑定的Service中回调获取数据的话,例如onReceive(in byte[] data)
,也要加一个“in”修饰,而如果参数类型是简单数据类型:String,int等就不需要,在用Androidstudio建aidl文件的时候会有默认提示:

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

表示int,long,boolean,float,double,String就可以直接传递,不需要加修饰符,当然double部数据简单数据类型

缺点:AIDL接口不够灵活,如果要传复杂的数据对象很麻烦,要定义对应的AIDL接口,而且在两个进程之中都要定义才行,更重要的是AIDL接口不稳定,因为Service有可能被系统杀死,这时候Service就是停止状态,然而要是这时候程序调用AIDL接口就会报DeadObjectException异常;会报空指针错误:

/**
    * 发送数据
    */
@Override public int send(byte[] data) 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.writeByteArray(data);
mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

这段代码是前面定义的send()的AIDL接口编译后自动生成的一个类,有时候就会报__reply.readException()是NullPointerExeception,解决方案:

@Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            try {
                return super.onTransact(code, data, reply, flags);
            } catch(RuntimeException e) {
                SyncLogUtil.e("AIDL接口异常,终于捕获到了");
                SyncLogUtil.e(e);
                throw e;
            }
        }
    }

捕获aidl异常,然后查看log分析到底是那一句报错,aidl接口定义好了之后就在service里面建一个内部累,集成aidl的Sub:

private class ConnectionBinder extends IConnectionService.Stub {

        @Override
        public int connect(String hostname, int port) throws RemoteException {
            return ConnectionService.this.connect(hostname, port, true);
        }

        @Override
        public int send(byte[] data) throws RemoteException {
            return ConnectionService.this.send(data);
        }

        @Override
        public void heartbeat() throws RemoteException {
            ConnectionService.this.heartbeat();
        }

        @Override
        public void close() throws RemoteException {
            ConnectionService.this.close();
        }

        @Override
        public void closeForBack(ICloseCallback icloseCallback) throws RemoteException {
            ConnectionService.this.close(icloseCallback);
        }

        @Override
        public boolean isClose() throws RemoteException {
            return ConnectionService.this.isClose();
        }

        @Override
        public void initServerInfo(String[] serverInfo) throws RemoteException {
            ConnectionService.this.initServerInfo(serverInfo);
        }

        @Override
        public void switchHost() throws RemoteException {
            ConnectionService.this.switchHost();
        }

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            try {
                return super.onTransact(code, data, reply, flags);
            } catch(RuntimeException e) {
                SyncLogUtil.e("AIDL接口异常,终于捕获到了");
                SyncLogUtil.e(e);
                throw e;
            }
        }
    }

这个就是提供给ingyige进程调用的远程接口了,而且在onBind()方法里面要反悔这个Binder:

@Override
    public IBinder onBind(Intent intent) {
        SyncLogUtil.d("TCPConnection Service has binded.");
        bind = true;
        return new ConnectionBinder();
    }

然后在另一个进程实现ServiceConnection中的两个方法:

public final class IConnServiceConnection implements ServiceConnection {

        private String[] serverInfo;

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            SyncLogUtil.d(name.getPackageName() + ":connect to service successfully.");
            connectionService = IConnectionService.Stub.asInterface(service);

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

       }

    }

connectionService 对象才是真正可以调用刚才定义的哪些aidl接口的对象,它就相当于外部进程和service之际嗯的桥梁,最后在startService,bindService就可以正常调用aidl接口了

3.LocalSocket和LocalServerSocket

这两个可实现和c进程的通信,比较灵活,因为数据传输是流式传输,用法和Socket,ServerSocket比较像,但是内部实现原理和SocketServerSocket就差很多了,虽然是好用,但是貌似调用close()方法的时候并不能像Socket那样直接就抛出异常,例如:

private void startLocalClient() throws IOException {
        localSocket = new LocalSocket();
        TLVByteBuffer buffer = new TLVByteBuffer();
        byte[] receiveBuf = new byte[512];
        int readLength = -1;
        localSocket.connect(new LocalSocketAddress("com.xtc.watch.local"));
        SyncLogUtil.i("LocalPush", "connect to local server success");
        inputStream = localSocket.getInputStream();
        outputStream = localSocket.getOutputStream();
        while ((readLength = inputStream.read(receiveBuf)) != -1) {
            buffer.write(receiveBuf, 0, readLength);
            SyncLogUtil.i("LocalPush", "local client read [" + readLength + "] bytes,all buffer bytes:" + buffer.size());
            while (buffer.hasNextTLVData()) {
                byte[] data = buffer.cutNextTLVData();
                if (data != null && data.length > 0) {
                    onRead(data);
                } else {
                    SyncLogUtil.e("LocalPush", "local client data read completely,but cutted tlv bytes is null or length = 0.");
                }
            }
        }
        SyncLogUtil.w("LocalPush", "local connect client readLength:" + readLength);
        buffer.reset();
    }

这里客户端的localSocket如果调用close()方法或者调用localSocket.getInputStream().close()程序并不会抛出IOExeception,而是继续堵在read()方法,这样就会造成线程一直阻塞,线程isAlive()返回的是true,线程资源垡释放,LocalServerSocket也是同样的问题,我尝试在调用close()方法后不断的调用localSocket和localServerSocketwrite数据,这样线程就能正常抛出异常然后结束,而且在LocalServerSocket初始化后会绑定到一个地址,调用close()之后并不会释放这个地址,此时再次绑定这个地址会报address already used,但是在调用了LocalServerSocket的close()方法之后再次连接一个LocalSocket进来,是可以正常连上的,不过此时LocalServerSocket已经把绑定的地址释放了,而且也不能喜剧读取和写入数据了,这时候再次初始化一个LocalServerSocket丙丁岛相同的地址就能绑定成功,但是你要是new一个LocalSocket,写完数据后立即关闭,那这时候客户端的就不会造成阻塞,会正常抛出IO异常,线程结束,关于LocalSocket和LocalServerSocket的正确用法还要继续研究,主要难点就是当读取等待的线程不用了,怎么才能成功close掉,这样才能成功释放线程资源,待补充。。。

直接用本地的TCP:Socket和ServerSocket

socket实际上是用于网络通信的套接字,但是如果你把手机当作服务器,那么实际上进程间的通信也可以采用网络套接字来实现,因为Socket的通信的基于网络的通信,不存在什么进程概念,这里只需要获取手机本地的IP,然后创建ServerSocket,再用Socket连接上即可,跟网络通信的Socket使用方式一模一样,而且这条TCP连接还可实现进程间的互相监听,当有一方进程挂掉的时候,TCP连接久断开了,另一个进程能及时的受到反馈,这个就比LoccalSocket的反馈及时很多,LocalSocket其实现原理和Socket大不一样,后面有时间再做补充。另外,微信目前对于进程间的通信采用的是AIDL来实现,但是据我在一个课程上了解他们认为Socket来实现进程间的通信是非常好的,可值得尝试的做法,他们今后也会在这一个方面做尝试

直接用本地UDP

因为本地UDP数据传输并不用ping到网络上,都是本地发包与收包,因此丢包的概率大大降低了,而且效率也比TCP数据传输高

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值