美文网首页
Android 中线程处理的问题

Android 中线程处理的问题

作者: sunjiandev | 来源:发表于2019-01-23 11:11 被阅读8次

    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();
                    }
                }
            }
        }
    

    接这样吧!

    相关文章

      网友评论

          本文标题:Android 中线程处理的问题

          本文链接:https://www.haomeiwen.com/subject/kwljjqtx.html