Android中对后台任务线程性能的说明及优化

What Triggers ANR?


Generally, the system displays an ANR if an application cannot respond to user input. For example, if an application blocks on some I/O operation (frequently a network access) on the UI thread so the system can't process incoming user input events. Or perhaps the app spends too much time building an elaborate in-memory structure or computing the next move in a game on the UI thread. It's always important to make sure these computations are efficient, but even the most efficient code still takes time to run.

In any situation in which your app performs a potentially lengthy operation, you should not perform the work on the UI thread, but instead create a worker thread and do most of the work there. This keeps the UI thread (which drives the user interface event loop) running and prevents the system from concluding that your code has frozen. Because such threading usually is accomplished at the class level, you can think of responsiveness as a class problem. (Compare this with basic code performance, which is a method-level concern.)

In Android, application responsiveness is monitored by the Activity Manager and Window Manager system services. Android will display the ANR dialog for a particular application when it detects one of the following conditions:

  • No response to an input event (such as key press or screen touch events) within 5 seconds.
  • BroadcastReceiver hasn't finished executing within 10 seconds.

How to Avoid ANRs


Android applications normally run entirely on a single thread by default the "UI thread" or "main thread"). This means anything your application is doing in the UI thread that takes a long time to complete can trigger the ANR dialog because your application is not giving itself a chance to handle the input event or intent broadcasts.

Therefore, any method that runs in the UI thread should do as little work as possible on that thread. In particular, activities should do as little as possible to set up in key life-cycle methods such as onCreate() and onResume(). Potentially long running operations such as network or database operations, or computationally expensive calculations such as resizing bitmaps should be done in a worker thread (or in the case of databases operations, via an asynchronous request).

The most effecive way to create a worker thread for longer operations is with the AsyncTask class. Simply extend AsyncTask and implement the doInBackground() method to perform the work. To post progress changes to the user, you can call publishProgress(), which invokes theonProgressUpdate() callback method. From your implementation of onProgressUpdate() (which runs on the UI thread), you can notify the user. For 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, simply create an instance and call execute():

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

Although it's more complicated than AsyncTask, you might want to instead create your own Threador HandlerThread class. If you do, you should set the thread priority to "background" priority by calling Process.setThreadPriority() and passing THREAD_PRIORITY_BACKGROUND. If you don't set the thread to a lower priority this way, then the thread could still slow down your app because it operates at the same priority as the UI thread by default.

--备注:在run方法开始处添加Process.setThreadPriority() 

If you implement Thread or HandlerThread, be sure that your UI thread does not block while waiting for the worker thread to complete—do not call Thread.wait() or Thread.sleep(). Instead of blocking while waiting for a worker thread to complete, your main thread should provide aHandler for the other threads to post back to upon completion. Designing your application in this way will allow your app's UI thread to remain responsive to input and thus avoid ANR dialogs caused by the 5 second input event timeout.

The specific constraint on BroadcastReceiver execution time emphasizes what broadcast receivers are meant to do: small, discrete amounts of work in the background such as saving a setting or registering a Notification. So as with other methods called in the UI thread, applications should avoid potentially long-running operations or calculations in a broadcast receiver. But instead of doing intensive tasks via worker threads, your application should start an IntentService if a potentially long running action needs to be taken in response to an intent broadcast.

Tip: You can use StrictMode to help find potentially long running operations such as network or database operations that you might accidentally be doing your main thread.

Reinforce Responsiveness


Generally, 100 to 200ms is the threshold beyond which users will perceive slowness in an application. As such, here are some additional tips beyond what you should do to avoid ANR and make your application seem responsive to users:

  • If your application is doing work in the background in response to user input, show that progress is being made (such as with a ProgressBar in your UI).
  • For games specifically, do calculations for moves in a worker thread.
  • If your application has a time-consuming initial setup phase, consider showing a splash screen or rendering the main view as quickly as possible, indicate that loading is in progress and fill the information asynchronously. In either case, you should indicate somehow that progress is being made, lest the user perceive that the application is frozen.
  • Use performance tools such as Systrace and Traceview to determine bottlenecks in your app's responsiveness。

对于Android平台上的线程优先级设置来说可以处理很多并发线程的阻塞问题,比如很多无关紧要的线程会占用大量的CPU时间,虽然通过了 MultiThread来解决慢速I/O但是合理分配优先级对于并发编程来说十分重要。Android在线程方面主要使用的是Java本身的Thread 类,我们可以在Thread或Runnable接口中的run方法首句加入
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //设置线程优先级为后台,这样当多个线程并发后很多无关紧要的线程分配的CPU时间将会减少,有利于主线程的处理,相关的Thread优先级 Android123就Android平台专有的定义罗列有以下几种:
int THREAD_PRIORITY_AUDIO //标准音乐播放使用的线程优先级
int THREAD_PRIORITY_BACKGROUND //标准后台程序
int THREAD_PRIORITY_DEFAULT // 默认应用的优先级
int THREAD_PRIORITY_DISPLAY //标准显示系统优先级,主要是改善UI的刷新
int THREAD_PRIORITY_FOREGROUND //标准前台线程优先级
int THREAD_PRIORITY_LESS_FAVORABLE //低于favorable
int THREAD_PRIORITY_LOWEST //有效的线程最低的优先级
int THREAD_PRIORITY_MORE_FAVORABLE //高于favorable
int THREAD_PRIORITY_URGENT_AUDIO //标准较重要音频播放优先级
int THREAD_PRIORITY_URGENT_DISPLAY //标准较重要显示优先级,对于输入事件同样适用

你可能感兴趣的:(android)