Avoid unresponsive methods in Android development (Application Not Responding ANR)

  • 2020-06-01 11:01:15
  • OfStack

The worst thing that can happen in App is that it pops up the "Application Not Responding" (ANR) dialog. This lesson is on how to keep your application responsive and avoid ANR.

What triggers ANR

Typically, the system displays ANR when an application is unable to respond to user input. For example, if an application blocks the (frequently requested network)UI thread on an I/O operation, the system cannot handle user input events. Or, in the UI thread, app spends a lot of time building complex classes or calculating the next action in the game. It's important to make these operations efficient, but even the most efficient code takes time.

Under no circumstances should a time-consuming task be performed on the UI thread. Instead, create a worker thread and operate on it. This keeps the UI thread running and prevents the system from ending the application because the code is stuck.
In Android, Activity Manager and Window Manager system services monitor the responsiveness of applications. When Android detects 1 of the following situations, the ANR dialog box pops up:

1. Failed to respond to the user input event within 5 seconds
2.BroadcastReceiver was not completed in 10 seconds

How to avoid ANR

The Android application runs in a single thread by default, called the UI thread or main thread. This means that all of your application's work is in the UI thread, and if it takes a long time to complete, ANR will be triggered because the application cannot control input events or broadcasts.

UI thread in any way, therefore, should as far as possible do the work of lightweight, especially Activity in lifecycle methods, like onCreate (), onResume (). Potentially time-consuming operation, such as network, database, or expensive computing (like change the picture size) should be done in a worker thread (or in the case of database operations, through an asynchronous requests).

The most efficient way is to create worker threads for time-consuming operations using the AsyncTask class. Inherit AsyncTask to implement the doInBackground() method to perform the work. To send progress to the user, call publishProgress(), which triggers onProgressUpdate(). Example:


private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
 
    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
 
    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

To execute this worker thread, you only need to create one instance and call execute():

new DownloadFilesTask().execute(url1, url2, url3);

Even though it's more complicated than AsyncTask, you might want to create your own thread or HandlerThread class, and if you do, you should call Process.setThreadPriority (THREAD_PRIORITY_BACKGROUND) and set the thread priority to "background".

If you implement Thread or HandlerThread, make sure the UI thread does not block while waiting for the worker thread to finish executing. Instead of calling Thread.wait () or Thread.sleep (), provide one Handler for callbacks when the task is finished. By design, the UI thread remains responsive, avoiding the ANR dialog.

A particular emphasis on BroadcastReceiver's execution time means you have to: spread out your work into background threads, like saving Settings or registering Notification. For intensive tasks (intensive tasks), IntentService should be used.

Tip: you can use StrictMode to help you find potentially time-consuming operations on the UI thread


Related articles: