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也在主线程上执行。您似乎在那里进行了阻塞操作,这可能是您遇到问题的原因。