Android中的进程之间的通信
2016-03-24 23:58
627 查看
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数据传输高相关文章推荐
- Android中ListView的优化
- Android Volley完全解析(一),初识Volley的基本用法
- Android自定义控件——3D画廊和图像矩阵
- Activity中ScrollView Fragment切换界面跳动问题
- Android 高清加载巨图方案 拒绝压缩图片
- Android Accessibility实现程序锁功能
- Android 5.0和6.0以及7.0预览版的特性(从google翻译)
- ProgressDialog的详细使用总结
- Andorid-foreground 解析
- 简述 Android 手势(Gesture)
- Android中StackView的使用
- AndroidStudio去掉自己写的未使用的方法下的波浪线
- Android中Spinner的使用
- Android四大组件之Broadcast使用及运行机制
- Android 报错Android - Performing stop of activity that is not resumed
- 如何让程序在Nexus上全屏显示
- Android基础——自定义EditTExt实现去掉输入框添加下划线
- Android 雷达扫描动画效果实现
- Android 自定义控件之第一讲:attr format 取值类型
- Android——LayoutParams的用法