0

So in my android application, I have an intent service which pings devices and finds whether they are online/offline.

When I start my IntentService my UI freezes(Debug points to when ping commands are being executed) in the Service.

Service is started from the parent activity after I get the response of a network call

loadFragment(printersFrag, Constants.CONTAINER_ACT_DASHBOARD, PrintersListingFragment.class.getSimpleName(), false, false, false);

serviceIntent = new Intent(this, PrinterPingIntentService.class);
serviceIntent.putExtra("PrinterList", printersResponse);
this.startService(serviceIntent);

The code for my IntentService is as follows:

public class PrinterPingIntentService extends IntentService {
    /**
     * The IP Address to ping
     */
    private String msIPAddressToPing = null;

    /**
     * Countdown latch instance to decrement after the thread is done
     */
    private CountDownLatch mCountDownLatch;
    /**
     * Handler to handle ping threads
     */
    private PingHandler mPingThreadHandler = null;

    /**
     * Volatile count variable to manage the ping thread count
     */
    private volatile int mnPingThreadCount = 0;
    /**
     * The currently list of valid IP Addresses
     */
    private ConcurrentHashMap<String, Device> mPrinterMap = new ConcurrentHashMap<String, Device>();

    public PrinterPingIntentService() {
        super(PrinterPingIntentService.class.getName());
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Bundle bundle = intent.getExtras();
        PrintersResponseBean printerResponse = bundle.getParcelable("PrinterList");
        for (int i = 0; i < printerResponse.getDevices().size(); i++) {
            mPrinterMap.put(printerResponse.getDevices().get(i).getDeviceIP(), printerResponse.getDevices().get(i));
        }

        validatePrinterIP();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        /*
        * Fire up the Ping handler
        */
        mPingThreadHandler = new PingHandler();
    }

    /**
     * Validate the PrinterIPs by pinging them
     *
     * @author 
     */
    private void validatePrinterIP() {
        try {
            mnPingThreadCount = 0;
            mCountDownLatch = new CountDownLatch(mPrinterMap.size());
            for (String sIP : mPrinterMap.keySet()) {
                PingRunnable runnable = new PingRunnable(sIP, mCountDownLatch);
                Thread thread = new Thread(runnable);
                ++mnPingThreadCount;
                Log.d("BAT", "validatePrinterIP - Thread count - " + mnPingThreadCount);
                thread.start();
            }
        } catch (Exception e) {
            Log.d("BAT", "Exception validatePrinterIP - " + e.getMessage());
        }
    }

    /**
     * Runnable to make a ping to the given Ip Address
     *
     * @author 
     */
    public class PingRunnable implements Runnable {
        ////////////////////////////////// CLASS MEMBERS ///////////////////////////////////////////
        /**
         * The IP Address to ping
         */
        private String msIPAddressToPing = null;

        /**
         * Countdown latch instance to decrement after the thread is done
         */
        private CountDownLatch mCountDownLatch;

        ////////////////////////////////// CLASS METHODS ///////////////////////////////////////////
        public PingRunnable(String sIPAddress, CountDownLatch latch) {
            msIPAddressToPing = sIPAddress;
            mCountDownLatch = latch;
        }

        @Override
        public void run() {
            try {
                /*
                 * If the destination is not reachable, remove the IP address
                 * from the printer map and set the bundle value accordingly
                 */
                if (!pingURL(msIPAddressToPing)) {
                    Log.d("BAT", "Could not ping " + msIPAddressToPing + ". Removing from Map");
                    mPrinterMap.remove(msIPAddressToPing);
                } else {
                    Log.d("BAT", "Could ping " + msIPAddressToPing + ". Present in Map");
                }
            } catch (Exception e) {
                Log.d("BAT", "Exception in Ping Runnable - " + e.getMessage());
            } finally {
                mPingThreadHandler.sendEmptyMessage(0);
                mCountDownLatch.countDown();
            }
        }
    }

    /**
     * Static Handler class to handle messsages.
     * Reduce the count by one each time we receive a message to keep
     * track that all threads have returned
     *
     * @author 
     */
    public class PingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.d("BAT", "Returning thread..");
            if (msg.what == 0) {
                mnPingThreadCount--;
                Log.d("BAT", "Thread Return count - " + mnPingThreadCount);
            }

            /*
            Await Latch
             */
            try {
                mCountDownLatch.await();
            } catch (InterruptedException e) {
                Log.d("BAT", "InterruptedException PingHandler - " + e.getMessage());
            }

            if (mnPingThreadCount == 0) {
                //////TEMP
                Log.d("BAT", "All threads accounted for. Final Printer List...");
                ArrayList<Device> onlinePrinters = new ArrayList<>();
                for (String sIP : mPrinterMap.keySet()) {
                    onlinePrinters.add(mPrinterMap.get(sIP));
                    Log.d("BAT", "Printers Active " + sIP);
                }

                //send data back to fragment via localBroadcastReceiver
                Intent localBroadcast = new Intent();
                localBroadcast.putParcelableArrayListExtra("onlinePrinters", onlinePrinters);
                localBroadcast.setAction("printer");
                sendBroadcast(localBroadcast);
            }
        }
    }

    /**
     * Ping a device. First we try the usual isReachable method. If that does not work,
     * we go with the Ping command execution
     *
     * @param sURL THe uRL / IP Address to ping
     * @author 
     */
    public boolean pingURL(String sURL) {
        try {
            Log.d("BAT", "Pinging IP sURL");
            //First try with isReachable
            if (Inet4Address.getByName(sURL).isReachable(1000)) {
                Log.d("BAT", "Host Reachable by InetAddress " + sURL);
                return true;
            }
            //else try and ping. If neither works, we return false
            else {
                Log.d("BAT", "Host Not Reachable by InetAddress. Pinging IP with RunTime... " + sURL);
                StringBuffer echo = new StringBuffer();
                Runtime runtime = Runtime.getRuntime();
                Process proc = runtime.exec("ping -c 1 " + sURL);
                // "/system/bin/ping -c 8 "  + sURL
                int nReturnVal = proc.waitFor();
                Log.d("BAT", "Done Pinging - " + sURL + ((nReturnVal == 0) ? " Successful" : " Unsuccessful"));
                return (nReturnVal == 0);
            }
        } catch (IOException e) {
            Log.d("BAT", "IOEXception in pingURL - " + e.getMessage().toString());
        } catch (InterruptedException e) {
            Log.d("BAT", "InterruptedException in pingURL - " + e.getMessage());
        } catch (Exception e) {
            Log.d("BAT", "EXception in pingURL - " + e.getMessage());
        }
        return false;
    }
}

From my intent service, I send back the data of active devices to my Fragment using:

//send data back to fragment via localBroadcastReceiver
Intent localBroadcast = new Intent();
localBroadcast.putParcelableArrayListExtra("onlinePrinters", onlinePrinters);
localBroadcast.setAction("printer");
sendBroadcast(localBroadcast);

and extract this info in my Fragment using:

IntentFilter filter = new IntentFilter();
filter.addAction("printer");
updateUIReceiver = new BroadcastReceiver() {
   @Override
   public void onReceive(Context context, Intent intent) {
     //UI update here
     Bundle bundle = intent.getExtras();
     if (bundle.get("onlinePrinters") != null) {
        onlinePrinters = (ArrayList) bundle.get("onlinePrinters");
        setPrinterStatus();
     }
   }
};

As I'm using an IntentService a UI freeze should be unlikely as the task is performed on a worker thread and not in the Main Thread.

Not able to figure out the root cause of the UI freeze

ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
usr30911
  • 2,731
  • 7
  • 26
  • 56

1 Answers1

1
  • onCreate() method of your service is called on the main thread.
  • PingHandler instance that you're creating there is associated with the main thread.
  • So handleMessage for this handler is also executed on the main thread. You seem to have blocking operations there which may be the cause of your problem.
algrid
  • 5,600
  • 3
  • 34
  • 37
  • If i create PingHandler instance in onHandleIntent the pingHandler fires just once , any pointers how i can start the pinghandler off the main thread? – usr30911 Aug 31 '17 at 14:35
  • Wouldn't it be ok for you to do everything in onHandleIntent without handlers and other threads? – algrid Aug 31 '17 at 14:41
  • True i can try that,initially i had written a service,when i figured out service runs on the main thread i created threads for each device pinging ,then i changed it to an intentService. – usr30911 Aug 31 '17 at 14:44
  • 1
    IntentService automatically creates a background thread for your. And onHandleInent is executed on that thread. You shouldn't create any additional threads manually. – algrid Aug 31 '17 at 14:47
  • i need multiple threads because i need to ping each device individually on a thread of its own, if i just iterate and ping each ip in my map it takes ages as devices range between 50-100, by creating threads per ip i get results much faster :( – usr30911 Aug 31 '17 at 17:34
  • Do you have to use a Service? It may be easier with AsyncTask. Using AsyncTask to allow parallelism you should consider this https://stackoverflow.com/a/13800208/7132300 – algrid Aug 31 '17 at 18:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153401/discussion-between-vivekh-and-algrid). – usr30911 Aug 31 '17 at 18:26