您的位置:首页 > 移动开发 > Android开发

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数据传输高
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: