diff --git a/docs/html/topic/performance/threads.jd b/docs/html/topic/performance/threads.jd new file mode 100644 index 0000000000000..b2a2d9f44b725 --- /dev/null +++ b/docs/html/topic/performance/threads.jd @@ -0,0 +1,453 @@ +page.title=Threading Performance +@jd:body + +
+
+ +

In this document

+
    +
  1. Main Thread +
      +
    1. Internals
    2. +
    +
  2. +
  3. Threading and UI Object References +
      +
    1. Explicit references
    2. +
    3. Implicit references
    4. +
    +
  4. +
  5. Threading and App and Activity Lifecycles +
      +
    1. Persisting threads
    2. +
    3. Thread priority
    4. +
    +
  6. +
  7. Helper Classes for Threading +
      +
    1. The AsyncTask class
    2. +
    3. The HandlerThread class
    4. +
    5. The ThreadPoolExecutor class
    6. +
    +
  8. +
+
+
+ +

+Making adept use of threads on Android can help you boost your app’s +performance. This page discusses several aspects of working with threads: +working with the UI, or main, thread; the relationship between app lifecycle and +thread priority; and, methods that the platform provides to help manage thread +complexity. In each of these areas, this page describes potential pitfalls and +strategies for avoiding them. +

+

Main Thread

+

+When the user launches your app, Android creates a new Linux +process along with an execution thread. This main thread, +also known as the UI thread, is responsible for everything that happens +onscreen. Understanding how it works can help you design your app to use the +main thread for the best possible performance. +

+

Internals

+

+The main thread has a very simple design: Its only job is to take and execute +blocks of work from a thread-safe work queue until its app is terminated. The +framework generates some of these blocks of work from a variety of places. These +places include callbacks associated with lifecycle information, user events such +as input, or events coming from other apps and processes. In addition, app can +explicitly enqueue blocks on their own, without using the framework. +

+

+Nearly any +block of code your app executes is tied to an event callback, such as input, +layout inflation, or draw. When something triggers an event, the thread where the event +happened pushes the event out of itself, and into the main thread’s message +queue. The main thread can then service the event. +

+ +

+While an animation or screen update is occurring, the system tries to execute a +block of work (which is responsible for drawing the screen) every 16ms or so, in +order to render smoothly at 60 +frames per second. For the system to reach this goal, some operations must +happen on the main thread. However, when the main thread’s messaging queue +contains tasks that are either too numerous or too long for the main thread to +complete work within the 16ms window, the app should move this work to a worker +thread. If the main thread cannot finish executing blocks of work within 16ms, +the user may observe hitching, lagging, or a lack of UI responsiveness to input. +If the main thread blocks for approximately five seconds, the system displays +the Application +Not Responding (ANR) dialog, allowing the user to close the app directly. +

+

+Moving numerous or long tasks from the main thread, so that they don’t interfere +with smooth rendering and fast responsiveness to user input, is the biggest +reason for you to adopt threading in your app. +

+

Threading and UI Object References

+

+By design, Android +UI objects are not thread-safe. An app is expected to create, use, and +destroy UI objects, all on the main thread. If you try to modify +or even reference a UI object in a thread other than the main thread, the result +can be exceptions, silent failures, crashes, and other undefined misbehavior. +

+

+Issues with references fall into two distinct categories: explicit references +and implicit references. +

+

Explicit references

+

+Many tasks on non-main threads have the end goal of updating UI objects. +However, if one of these threads accesses an object in the view hierarchy, +application instability can result: If a worker thread changes the properties of +that object at the same time that any other thread is referencing the object, +the results are undefined. +

+

+For example, consider an app that holds a direct reference to a UI object on a +worker thread. The object on the worker thread may contain a reference to a +{@link android.view.View}; but before the work completes, the {@link android.view.View} is +removed from the view hierarchy. When these two actions happen simultaneously, +the reference keeps the {@link android.view.View} object in memory and sets properties on it. +However, the user never sees +this object, and the app deletes the object once the reference to it is gone. +

+ +

+In another example, {@link android.view.View} objects contain references to the activity +that owns them. If +that activity is destroyed, but there remains a threaded block of work that +references it—directly or indirectly—the garbage collector will not collect +the activity until that block of work finishes executing. +

+

+This scenario can cause a problem in situations where threaded work may be in +flight while some activity lifecycle event, such as a screen rotation, occurs. +The system wouldn’t be able to perform garbage collection until the in-flight +work completes. As a result, there may be two {@link android.app.Activity} objects in +memory until garbage collection can take place. +

+ +

+With scenarios like these, we suggest that your app not include explicit +references to UI objects in threaded work tasks. Avoiding such references helps you avoid +these types of memory leaks, while also steering clear of threading contention. +

+

+In all cases, your app should only update UI objects on the main thread. This +means that you should craft a negotiation policy that allows multiple threads to +communicate work back to the main thread, which tasks the topmost activity or +fragment with the work of updating the actual UI object. +

+

Implicit references

+

+A common code-design flaw with threaded objects can be seen in the snippet of +code below: +

+
+public class MainActivity extends Activity {
+  // …...
+  public class MyAsyncTask extends AsyncTask   {
+    @Override protected String doInBackground(Void... params) {...}
+    @Override protected void onPostExecute(String result) {...}
+  }
+}
+
+

+The flaw in this snippet is that the code declares the threading object +{@code MyAsyncTask} as an inner class of some activity. This declaration creates an +implicit reference to the enclosing {@link android.app.Activity} object. +As a result, the object contains a reference to the activity until the +threaded work completes, causing a delay in the destruction of the referenced activity. +This delay, in turn, puts more pressure on memory. +

+

+A direct solution to this problem would be to define your overloaded class +instances in their own files, thus removing the implicit reference. +

+

+Another solution is to declare the {@link android.os.AsyncTask} object +as a static nested class. Doing so eliminates the implicit reference problem +because of the way a static nested +class differs from an inner class: An instance of an inner class requires an +instance of the outer class to be instantiated, and has direct access to the +methods and fields of its enclosing instance. By contrast, a static nested class +does not require a reference to an instance of enclosing class, so it contains +no references to the outer class members. +

+
+public class MainActivity extends Activity {
+  // …...
+  Static public class MyAsyncTask extends AsyncTask   {
+    @Override protected String doInBackground(Void... params) {...}
+    @Override protected void onPostExecute(String result) {...}
+  }
+}
+
+

Threading and App and Activity Lifecycles

+

+The app lifecycle can affect how threading works in your application. +You may need to decide that a thread should, or should not, persist after an +activity is destroyed. You should also be aware of the relationship between +thread prioritization and whether an activity is running in the foreground or +background. +

+

Persisting threads

+

+Threads persist past the lifetime of the activities that spawn them. Threads +continue to execute, uninterrupted, regardless of the creation or destruction of +activities. In some cases, this persistence is undesirable. +

+

+Consider a case in which an activity spawns a set of threaded work blocks, and +is then destroyed before a worker thread can execute the blocks. What should the +app do with the blocks that are in flight? +

+ +

+If the blocks were going to update a UI that no longer exists, there’s no reason +for the work to continue. For example, if the work is to load user information +from a database, and then update views, the thread is no longer necessary. +

+ +

+By contrast, the work packets may have some benefit not entirely related to the +UI. In this case, you should persist the thread. For example, the packets may be +waiting to download an image, cache it to disk, and update the associated +{@link android.view.View} object. Although the object no longer exists, the acts of downloading and +caching the image may still be helpful, in case the user returns to the +destroyed activity. +

+ +

+Managing lifecycle responses manually for all threading objects can become +extremely complex. If you don’t manage them correctly, your app can suffer from +memory contention and performance issues. Loaders +are one solution to this problem. A loader facilitates asynchronous loading of +data, while also persisting information through configuration changes. +

+

Thread priority

+

+As described in Processes +and the Application Lifecycle, the priority that your app’s threads receive +depends partly on where the app is in the app lifecycle. As you create and +manage threads in your application, it’s important to set their priority so that +the right threads get the right priorities at the right times. If set too high, +your thread may interrupt the UI thread and RenderThread, causing your app to +drop frames. If set too low, you can make your async tasks (such as image +loading) slower than they need to be. +

+

+Every time you create a thread, you should call +{@link android.os.Process#setThreadPriority(int, int) setThreadPriority()}. +The system’s thread +scheduler gives preference to threads with high priorities, balancing those +priorities with the need to eventually get all the work done. Generally, threads +in the foreground +group get about 95% of the total execution time from the device, while the +background group gets roughly 5%. +

+

+The system also assigns each thread its own priority value, using the +{@link android.os.Process} class. +

+

+By default, the system sets a thread’s priority to the same priority and group +memberships as the spawning thread. However, your application can explicitly +adjust thread priority by using +{@link android.os.Process#setThreadPriority(int, int) setThreadPriority()}. +

+

+The {@link android.os.Process} +class helps reduce complexity in assigning priority values by providing a +set of constants that your app can use to set thread priorities. For example, THREAD_PRIORITY_DEFAULT +represents the default value for a thread. Your app should set the thread's priority to THREAD_PRIORITY_BACKGROUND +for threads that are executing less-urgent work. +

+

+Your app can use the THREAD_PRIORITY_LESS_FAVORABLE +and THREAD_PRIORITY_MORE_FAVORABLE +constants as incrementers to set relative priorities. A list of all of these +enumerated states and modifiers appears in the reference documentation for +the {@link android.os.Process#THREAD_PRIORITY_AUDIO} class. + +For more information on +managing threads, see the reference documentation about the +{@link java.lang.Thread} and {@link android.os.Process} classes. +

+

+https://developer.android.com/reference/android/os/Process.html#THREAD_PRIORITY_AUDIO +

+

Helper Classes for Threading

+

+The framework provides the same Java classes & primitives to facilitate +threading, such as the {@link java.lang.Thread} and +{@link java.lang.Runnable} classes. +In order to help reduce the cognitive load associated with +of developing threaded applications for +Android, the framework provides a set of helpers which can aide in development. +Each helper class has a specific set of performance nuances that make them +unique for a specific subset of threading problems. Using the wrong class for +the wrong situation can lead to performance issues. +

+

The AsyncTask class

+

+ +The {@link android.os.AsyncTask} class +is a simple, useful primitive for apps that need to quickly move work from the +main thread onto worker threads. For example, an input event might trigger the +need to update the UI with a loaded bitmap. An {@link android.os.AsyncTask} +object can offload the +bitmap loading and decoding to an alternate thread; once that processing is +complete, the {@link android.os.AsyncTask} object can manage receiving the work +back on the main thread to update the UI. +

+

+When using {@link android.os.AsyncTask}, there are a few important performance +aspects to keep in +mind. First, by default, an app pushes all of the {@link android.os.AsyncTask} +objects it creates into a +single thread. Therefore, they execute in serial fashion, and—as with the +main +thread—an especially long work packet can block the queue. For this reason, +we suggest that you only use {@link android.os.AsyncTask} to handle work items +shorter than 5ms in duration. +

+

+{@link android.os.AsyncTask} objects are also the most common offenders +for implicit-reference issues. +{@link android.os.AsyncTask} objects present risks related to explicit +references, as well, but these are +sometimes easier to work around. For example, an {@link android.os.AsyncTask} +may require a reference to a UI object in order to update the UI object +properly once {@link android.os.AsyncTask} executes its callbacks on the +main thread. In such a situation, you +can use a {@link java.lang.ref.WeakReference} +to store a reference to the required UI object, and access the object once the +{@link android.os.AsyncTask} is operating on the main thread. To be clear, +holding a {@link java.lang.ref.WeakReference} +to an object does not make the object thread-safe; the +{@link java.lang.ref.WeakReference} merely +provides a method to handle issues with explicit references and garbage +collection. +

+

The HandlerThread class

+

+While an {@link android.os.AsyncTask} +is useful, +it may not always be the right solution to your threading problem. Instead, +you may need a more traditional approach to executing a block of work on a +longer running thread, and some ability to manage that workflow manually. +

+ +

+Consider a common challenge with getting preview frames from your +{@link android.hardware.Camera} object. + When you register for Camera preview frames, you receive them in the + {@link android.hardware.Camera.PreviewCallback#onPreviewFrame(byte[], android.hardware.Camera) onPreviewFrame()} +callback, which is invoked on the event thread it was called from. If this +callback were invoked on the UI thread, the task of dealing with the huge pixel +arrays would be interfering with rendering and event processing work. The same +problem applies to {@link android.os.AsyncTask}, which also executes jobs serially and is +susceptible to blocking. +

+

+This is a situation where a handler thread would be appropriate: A handler thread +is effectively a long-running thread that grabs work from a queue, and operates +on it. In this example, when your app delegates the +{@link android.hardware.Camera#open Camera.open()} command to a +block of work on the handler thread, the associated + {@link android.hardware.Camera.PreviewCallback#onPreviewFrame(byte[], android.hardware.Camera) onPreviewFrame()} +callback +lands on the handler thread, rather than the UI or {@link android.os.AsyncTask} +threads. So, if you’re going to be doing long-running work on the pixels, this +may be a better solution for you. +

+

+When your app creates a thread using {@link android.os.HandlerThread}, don’t +forget to set the thread’s + +priority based on the type of work it’s doing. Remember, CPUs can only +handle a small number of threads in parallel. Setting the priority helps +the system know the right ways to schedule this work when all other threads +are fighting for attention. +

+

The ThreadPoolExecutor class

+

+There are certain types of work that can be reduced to highly parallel, +distributed tasks. One such task, for example, is calculating a filter for each +8x8 block of an 8 megapixel image. With the sheer volume of work packets this +creates, +{@code AsyncTask} and {@code HandlerThread} aren’t appropriate +classes. The single-threaded nature of {@link android.os.AsyncTask} would +turn all the threadpooled work into a linear system. +Using the {@link android.os.HandlerThread} class, on the other hand, would +require the programmer to manually manage load balancing between a group of +threads. +

+ +

+{@link java.util.concurrent.ThreadPoolExecutor} is a helper class to make +this process easier. This class manages the creation of a group of threads, sets +their priorities, and manages how work is distributed among those threads. +As workload increases or decreases, the class spins up or destroys more threads +to adjust to the workload. +

+

+This class also helps your app spawn an optimum number of threads. When it +constructs a {@link java.util.concurrent.ThreadPoolExecutor} +object, the app sets a minimum and maximum +number of threads. As the workload given to the +{@link java.util.concurrent.ThreadPoolExecutor} increases, +the class will take the initialized minimum and maximum thread counts into +account, and consider the amount of pending work there is to do. Based on these +factors, {@link java.util.concurrent.ThreadPoolExecutor} decides on how many +threads should be alive at any given time. +

+

How many threads should you create?

+

+Although from a software level, your code has the ability to create hundreds of +threads, doing so can create performance issues. CPUs really only have the +ability to handle a small number of threads in parallel; everything above that +runs +into priority and scheduling issues. As such, it’s important to only create +as many threads as your workload needs. +

+

+Practically speaking, there’s a number of variables responsible for this, but +picking a value (like 4, for starters), and testing it with Systrace is as +solid a strategy as any other. You can use trial-and-error to discover the +minimum number of threads you can use without running into problems. +

+

+Another consideration in deciding on how many threads to have is that threads +aren’t free: they take up memory. Each thread costs a minimum of 64k of memory. +This adds up quickly across the many apps installed on a device, especially in +situations where the call stacks grow significantly. +

+

+Many system processes and third-party libraries often spin up their own +threadpools. If your app can reuse an existing threadpool, this reuse may help +performance by reducing contention for memory and processing resources. +

+ +