UI 冻结 运行 IntentService 中的多个线程
UI Freezes Running multiple Threads in IntentService
所以在我的 android 应用程序中,我有一个 intent 服务,它可以 ping 设备并确定它们是否 online/offline。
当我启动我的 IntentService 时,我的 UI 在服务中冻结(调试指向执行 ping 命令的时间)。
服务是在我得到网络调用的响应后从父activity启动的
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);
我的IntentService的代码如下:
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;
}
}
从我的意图服务中,我使用以下方式将活动设备的数据发送回我的片段:
//send data back to fragment via localBroadcastReceiver
Intent localBroadcast = new Intent();
localBroadcast.putParcelableArrayListExtra("onlinePrinters", onlinePrinters);
localBroadcast.setAction("printer");
sendBroadcast(localBroadcast);
并使用以下方法在我的片段中提取此信息:
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();
}
}
};
由于我使用的是 IntentService,因此 UI 冻结应该不太可能发生,因为该任务是在工作线程上执行的,而不是在主线程中执行的。
无法找出 UI 冻结的根本原因
onCreate()
您服务的方法在主线程上被调用。
PingHandler
您在那里创建的实例与主线程相关联。
- 所以这个处理程序的
handleMessage
也在主线程上执行。您似乎在那里进行了阻塞操作,这可能是您遇到问题的原因。
所以在我的 android 应用程序中,我有一个 intent 服务,它可以 ping 设备并确定它们是否 online/offline。
当我启动我的 IntentService 时,我的 UI 在服务中冻结(调试指向执行 ping 命令的时间)。
服务是在我得到网络调用的响应后从父activity启动的
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);
我的IntentService的代码如下:
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;
}
}
从我的意图服务中,我使用以下方式将活动设备的数据发送回我的片段:
//send data back to fragment via localBroadcastReceiver
Intent localBroadcast = new Intent();
localBroadcast.putParcelableArrayListExtra("onlinePrinters", onlinePrinters);
localBroadcast.setAction("printer");
sendBroadcast(localBroadcast);
并使用以下方法在我的片段中提取此信息:
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();
}
}
};
由于我使用的是 IntentService,因此 UI 冻结应该不太可能发生,因为该任务是在工作线程上执行的,而不是在主线程中执行的。
无法找出 UI 冻结的根本原因
onCreate()
您服务的方法在主线程上被调用。PingHandler
您在那里创建的实例与主线程相关联。- 所以这个处理程序的
handleMessage
也在主线程上执行。您似乎在那里进行了阻塞操作,这可能是您遇到问题的原因。