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
onCreate()
method of your service is called on the main thread.PingHandler
instance that you're creating there is associated with the main thread.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.