Android 中线程处理的问题
Android 中线程处理的问题
先说一下问题发生的背景,公司有一款产品是终端自组网,通信是使用的sip协议,用户列表维护是用的udp 组播的方式实现,由于年代久远,代码不太友好,有好多坑,今天要说的这个就是最近返回的问题
问题描述
用户反馈,在链路通断的情况下有大概率情况,会出现,udp 消息接受不到,wireshark 抓包分析是由于,收到 udp消息之后,ip层 收到icmp 协议,提示 端口不可达,adb shell 进去之后,发现端口以及被释放,消息当然收不到
看一下代码实现逻辑是这样的:
@Override
public void run() {
super.run();
try {
socketReceive = new DatagramSocket(CommandType.RECV_PORT);
while (!socketReceive.isClosed() && null != socketReceive) {
recvBuffer = new byte[CommandType.bufferSize];
DatagramPacket rdp = new DatagramPacket(recvBuffer,
recvBuffer.length);
socketReceive.receive(rdp);
//业务处理
parsePackage(recvBuffer);
}
} catch (Exception e) {
e.printStackTrace();
try {
if (null != socketReceive && !socketReceive.isClosed()) {
socketReceive.close();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
代码貌似没什么问题,仔细分析了一下,应该是while循环关闭了,看了一下catch里面的内容,会不会是处理业务的时候抛了异常,导致端口socket 关闭?
于是模拟了一下场景:拿udp 发包工具往监听的端口发送大量数据,然后立即关闭网络连接,此时数据io读写还没有完成,必然会出现io exception ,然后socket 会释放
经过模拟测试,果然出现这问题,于是把异常处理的代码注释了不就行了,于是这样:
@Override
public void run() {
super.run();
try {
socketReceive = new DatagramSocket(CommandType.RECV_PORT);
while (!socketReceive.isClosed() && null != socketReceive) {
recvBuffer = new byte[CommandType.bufferSize];
DatagramPacket rdp = new DatagramPacket(recvBuffer,
recvBuffer.length);
socketReceive.receive(rdp);
//业务处理
parsePackage(recvBuffer);
}
} catch (Exception e) {
e.printStackTrace();
// try {
// if (null != socketReceive && !socketReceive.isClosed()) {
// socketReceive.close();
// }
// } catch (Exception e1) {
// e1.printStackTrace();
// }
}
}
再次模拟一下之前的那个场景,果然解决,so easy,打包,发布版本 完美!!!
第二天又反馈还是出现这个问题,只不过概率比原来小很多, wtf 看现场抓取的日志:
发现监听的消息的线程终止了,我 艹,光想这解决问题,没有从根本上想发生问题的原因,分析原因,之前解决应该是复现问题的一个场景,还有别的情况会导致线程中断,看代码:
@Override
public void run() {
super.run();
try {
socketReceive = new DatagramSocket(CommandType.RECV_PORT);
while (!socketReceive.isClosed() && null != socketReceive) {
recvBuffer = new byte[CommandType.bufferSize];
DatagramPacket rdp = new DatagramPacket(recvBuffer,
recvBuffer.length);
socketReceive.receive(rdp);
//业务处理
parsePackage(recvBuffer);//此处是业务逻辑处理,只要里面有异常发生,都会导致监听线程终止
}
} catch (Exception e) {
e.printStackTrace();
// try {
// if (null != socketReceive && !socketReceive.isClosed()) {
// socketReceive.close();
// }
// } catch (Exception e1) {
// e1.printStackTrace();
// }
}
}
看这个代码就知道,这个函数是在 线程内部实现的,其实这就是前辈挖的坑,正常应该是吧业务逻辑放在单独的线程处理,监听线程只负责服务数据获取,不应该操作业务逻辑,业务服务和业务分开,单独处理,跟服务器的大哥沟通了一下,发现他们就是这么处理的,虽然发现问题,但是我不打算改了,因为涉及改动太大,怕又引发其他潜在的bug,想一个代价最小的方式解决这个问题,都是前辈留下的坑!!!
于是采取如下方式解决该问题(强烈不建议使用):
在服务开启的时候,开一个监听线程,定期检测udp服务监听线程是否存活,如果不存活,重新开启,代码如下:
private class ObserverThread extends Thread{
@Override
public void run() {
while(isStartOK){
SystemClock.sleep(2000);
MyLog.e(TAG, "adhoc thread is alive :"+adhocCom.isAlive());
if (!adhocCom.isAlive()) {
adhocCom = new AdhocCommunication("adhoc");//此处一点要重新复制,虽然这个线程对象没有被销毁,
//但是已经不能被再次开启了,还有要注意的就是在之前的udp监听线程里面异常处理的代码放开,不然再次开启的时候,开启失败,端口被占用了
adhocCom.start();
}
}
}
}
接这样吧!