0

I have a simple thread listening to UDP broadcasts to discover devices on my network. Whenever it receives a broadcast, it posts a Runnable to a Handler on the UI thread to add the device's IP to an ArrayList. At most, it receives packets once every 2 seconds. I've verified that the posts only happen every 2 seconds.

My app also has a few fragments. Whenever I switch using NavController.navigate() between fragments I get anywhere from 100-400 frame drops and ANRs. When I disable the thread, or even just disable searching for duplicates and adding the device to the ArrayList, the frame drops go away, but the searching and adding take less than 1ms (according to debug prints).

I've read a bunch of other thread about problems with Handlers, but none of them helped. Any idea why my posts are causing so many frame drops?

Here's the run method of the Thread:

public void run(){
            try {
                DatagramSocket ds = new DatagramSocket(DISCOVERY_PORT);
                ds.setSoTimeout(500);
                ds.setBroadcast(true);
                byte[] rxBuffer = new byte[1024];
                DatagramPacket dp = new DatagramPacket(rxBuffer,rxBuffer.length);

                while (!this.isInterrupted()) {
                    try {
                        ds.receive(dp);

                        String msg = new String(rxBuffer, 0, dp.getLength());
                        dp.setLength(rxBuffer.length);
                        callbackH.post(new Runnable() {
                            @Override
                            public void run() {
                                addGeophone(dp.getAddress().toString(),Integer.parseInt(msg));
                            }
                        });
                    }
                    catch (SocketTimeoutException e){
                        continue;
                    }
                }

                ds.close();
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }

And the addGeophone function that gets posted to the UI thread:

    private static void addGeophone(String ipAddress, int id){
        boolean geophoneFound = false;
        Geophone g;
        //REMOVE BELOW HERE AND EVERYTHING SPEEDS UP.
        for (int i = 0; i < geophones.size(); i++){
            g = geophones.get(i);
            if (g.id == id){
                geophoneFound = true;
            }
        }
        if (!geophoneFound) {
            geophones.add(new Geophone(ipAddress, id));
       }
    }
Martin_xs6
  • 559
  • 3
  • 9
  • 1
    I don't see anything obviously wrong from the code you posted. How big is your geophones list? If it is large it might help to create a HashMap (or SparseArray) and index it by the ID. – Maurice Lam Jan 23 '21 at 18:42
  • Yeah, that's the thing, there's only like 4 elements in it, but it's still super slow.. – Martin_xs6 Jan 23 '21 at 20:06
  • 1
    Just another guess, maybe it's the constructor `new Geophone` that's slow? What is being done in that constructor? – Maurice Lam Jan 24 '21 at 08:34
  • That one should be fast. Right now the `Geophone` class is jut a `String`, an `int` and the constructor The constructor just assigns values to them. The whole `addGeophone` function takes almost no time at all, but only when it actually runs. – Martin_xs6 Jan 25 '21 at 00:55

1 Answers1

0

Looks like DatagramPacket.getAddress() takes a really long time if it's executed from another thread. When I moved the getAddress call to the poster thread, everything sped up.

public void run(){
            try {
                DatagramSocket ds = new DatagramSocket(DISCOVERY_PORT);
                ds.setSoTimeout(500);
                ds.setBroadcast(true);
                byte[] rxBuffer = new byte[1024];
                DatagramPacket dp = new DatagramPacket(rxBuffer,rxBuffer.length);
                while (!this.isInterrupted()) {
                    try {
                        ds.receive(dp);

                        String msg = new String(rxBuffer, 0, dp.getLength());
                        dp.setLength(rxBuffer.length);
                        String ipAddress = dp.getAddress().toString();
                        callbackH.post(new Runnable() {
                            @Override
                            public void run() {
                                addGeophone(ipAddress,Integer.parseInt(msg));
                            }
                        });
                    }
                    catch (SocketTimeoutException e){
                        continue;
                    }
                }

                ds.close();
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }
Martin_xs6
  • 559
  • 3
  • 9