Tải bản đầy đủ
Chapter 15. Summary: Selecting an Asynchronous Technique

Chapter 15. Summary: Selecting an Asynchronous Technique

Tải bản đầy đủ

Figure 15-1. The asynchronous mechanisms in the Android platform
When selecting the proper asynchronous mechanism, the rule of thumb is to move as
high up in the hierarchy as possible to utilize the added platform functionality. When
you need to give more execution control to the application, you can move to a lower
level, where that control is provided.

Keep It Simple
The Android platform contains a range of powerful asynchronous techniques that are
all based on the java.lang.Thread class. The techniques are there to make life easier
for us so that we don’t have to manage all the threading ourselves. Nevertheless, the
simplicity of a single thread can be a good choice for occassional one-shot tasks. For
example:
• Initializing an Activity with data from a network resource
• Storing data on the file system when Activity is destroyed
However, execution with Thread doesn’t scale well. If there are many tasks to execute,
a thread-per-task pattern can cause equally as many threads as there are tasks to run
simultaneously, which in turn can cause memory exhaustion and reduced performance.
Instead, it’s better to use a thread pool that easily can constrain the number of threads.

Thread and Resource Management
Thread pools are a part of Java’s Executor framework that helps us with thread execution
and resource management. Each pool holds and manages a set of worker threads, mak‐
ing their number of threads and dynamics configurable. Threads that have finished
executing a task can remain idle in the pool, waiting for the next task to execute. Hence,
the thread pool can keep required memory usage within bounds. The thread pool ach‐
244

|

Chapter 15: Summary: Selecting an Asynchronous Technique

ieves this by queuing the tasks that are added while all threads in the pool are occupied
by other tasks.
Thread pools are powerful asynchronous processors with a flexible setup. They are
typically used when the application should execute multiple tasks concurrently while
limiting the number of threads—for example, when the application uses multiple HTTP
connections that should have an upper limit. In combination with a Service, the pool
creates an asynchronous executor that can receive tasks from multiple clients and avoid
system termination.

Message Communication for Responsiveness
Both the Thread and the Executor framework are Java mechanisms and have no builtin support for Android message passing, which has to be implemented in the applica‐
tion. The most common use cases for asynchronous execution involve message passing
so that data can be passed between the UI thread and background threads.
The HandlerThread combines the execution of a Thread with the possibility of receiving
and processing messages sequentially. Hence, you need somewhat less coding to use
HandlerThread directly than to decorate a Thread with a Handler.
Messages are processed sequentially. This makes the HandlerThread a good alternative
for thread-safe execution of dependent tasks, which would require synchronization in
a concurrent environment. It’s also a very flexible sequential environment that makes it
easy to implement state-machine behavior, where the thread passes messages to itself
to change state or chain background tasks, as we saw in “Task Chaining” on page 128.
The sequential execution limitation of the HandlerThread is solved by AsyncTask, a
popular asynchronous technique. The AsyncTask allows background task execution in
combination with thread communication, offering a generic and adaptable asynchro‐
nous technique that can be applied on many use cases—the AsyncTask itself does not
impose any constraints. It has occasionally—and incorrectly—been given the epithet
“Android’s thread,” as it hides the message passing between threads.
A fully utilized AsyncTask passes data to the background thread, reports progress up‐
dates, and sends the result back to the UI thread. That also serves as a good use case.
However, as we saw in Chapter 10, it has a couple of concerns you need to consider:
• Global execution environment
• Differences between execution types depending on platform versions
The AsyncTask is often overused in applications due to its simplicity. It isn’t a silverbullet solution for asynchronous execution on Android. For many use cases, you should

Message Communication for Responsiveness

|

245

look into alternative techniques, for reasons of architecture, program design, or just
because they are less error prone.

Avoid Unexpected Task Termination
The system doesn’t take running threads into consideration when shutting down ap‐
plications to reclaim system resources. Therefore, background tasks can be unexpect‐
edly terminated when the application process is killed. The application termination is
based on process rank, and a running Service raises the application process rank—e.g.,
how likely an Activity is to be stopped when the application isn’t visible on the screen.
Use of a Service helps by reducing the risk that the application and its threads are
terminated when the system is low on resources.
There is no asynchronous executor in the Service; it’s a collaborator rather than a
competitor with other asynchronous mechanisms. By moving the asynchronous exe‐
cution to a Service, you can achieve several advantages:
• Critical background tasks execute independently of the application’s visibility on
the screen—i.e., the user’s interaction will be less likely to interfere with the lifetime
of the tasks.
• The asynchronous execution can easily be shared between multiple clients, such as
Activities.
• The asynchronous execution can be triggered both via Intents and through meth‐
od calls, or even across processes via IPC.
For services that should execute tasks sequentially, you can use IntentService, an ex‐
tension to Service containing a HandlerThread. It has the lifecycle and process rank
advantages of a Service combined with a handoff mechanism. The strength of the
IntentService is its simplicity: clients only have to send an Intent with data to be
processed.
The IntentService is a great candidate for sequential background execution for mul‐
tiple reasons:
• Simple usage.
• The process rank reduces the risk of background tasks being terminated prema‐
turely.
• It is reachable from all other Android components.
When asynchronous execution is required from a BroadcastReceiver, the IntentSer

vice should be the first mechanism you consider.

246

|

Chapter 15: Summary: Selecting an Asynchronous Technique

Easy Access to ContentProviders
The AsyncQueryHandler is an asynchronous ContentProvider accessor with a built-in
HandlerThread, which guarantees that the provider accesses the thread safely. It exists
specifically to handle a full set of CRUD operations on a provider and isn’t useful other‐
wise.
Another—more capable—reader of provider data is the CursorLoader. It is both simple
and powerful, not requiring much effort to implement. It connects to a provider and
observes the content so that new data can be loaded asynchronously to an Activity or
Fragment, enabling it to easily update the UI.
For other data sources—where you have to implement a custom loader—a loader isn’t
necessarily the best choice. It’s often more complex to implement a custom loader than
to adopt another asynchronous mechanism. As a rule of thumb, custom loaders can be
a good choice when the following conditions are fulfilled:
• The underlying content is easily observable.
• There should be an easy way to use a data cache.
• Started tasks don’t need to be finished, because the tight coupling of the loader with
the Activity and Fragment lifecycles will destroy the attached Loader objects and
lose the result.
Thus, it isn’t advisable, for example, to use loaders for network requests; they are not
easily observed and they will be interrupted based on the client lifecycle. Data loading
that should execute until completion should use a Service or IntentService instead.
The array of options in this book can be intimidating. One of the reasons for the variety
of asynchronous options is the wide range of considerations that go into different ap‐
plications: memory use, number of concurrent tasks, needs to access data stores, latency
issues, and so on. By studying the issues I’ve presented in the book and how they pertain
to your particular app, you can find the best technique to suit your needs.

Easy Access to ContentProviders

|

247

APPENDIX A

Bibliography

Books
• Brian Goetz et al. Java Concurrency in Practice. Addison-Wesley, 2006.

Articles
• Schreiber, Thorsten. “Android Binder: Android Interprocess Communication.”
Thesis, Ruhr-Universitat Bochum, 2011. http://bit.ly/1jgw0ui.
• Pepperdine, Kirk. “Tuning the Size of Your Thread Pool,” InfoQ. 2013. http://bit.ly/
1g5mefe.

Presentations
• Morrill, Dan. “Inside the Android Application Framework.” Google I/O, 2008.
• Bornstein, Dan. “Dalvik Virtual Machine Internals.” Google I/O, 2008.
• Dubroy, Patrick. “Memory Management for Android Apps.” Google I/O, 2011.

249

Index

Symbols

| (pipe operator), 39

A

activities
Activity implementing LoaderManager.Loa‐
derCallbacks, 221
Activity object, 3
BluetoothActivity (example), 189
BoundLocalActivity (example), 197
configuration changes on Activity objects,
114
DownloadActivity (example), 191
implementation of Activity displaying im‐
ages downloaded with AsyncTask, 164
loaders working with, 220
memory leaks from Activity objects, 96
one-way communication between Service
and Activity in different processes, 84
retaining worker threads from old to new
Activity components, 102
thread retention in an Activity, 114
two-way communication between Activity
and Service in different processes, 86
Activity.runOnUiThread() method, 73
ActivityThread class, 60

Android Interface Definition Language (AIDL),
76, 77
asynchronous RPC, 81
synchronous RPC, 79
Android platform, 1
asynchronous mechanisms in, 243
software stack, 1
android.os.FileObserver class, 236
anonymous inner class, 112
Application Framework, 2
Application Not Responding (ANR) dialog, 10
Application object, 3
application threads, 29
background threads, 30
binder threads, 30
memory leaks from, 91
UI thread, 29
applications, 2
Activities, 3
architecture, 2
components, 3
BroadcastReceiver, 5
ContentProvider, 5
execution, 5
lifecycle, 6
Linux process, 6
execution of AsyncTask instances, 169
Services, 5
structuring for performance, 9

We’d like to hear your suggestions for improving our indexes. Send email to index@oreilly.com.

251

ArrayBlockingQueue class, 140
ART (Android Runtime), 2
asynchronous operations, 9
asynchronous execution in BroadcastReceiv‐
er, 204–207
Remote Procedure Calls (RPC), 81
selecting an ansynchronous technique, 243
avoiding unexpected task termination,
246
easy access to ContentProviders, 247
message communication for responsive‐
ness, 245
thread and resource management, 244
using a Service for asynchronous execution,
177
asynchronous programming, 105–119
choosing an asynchronous technique for
Services, 197
lifecycle of a thread, 107
managing threads, 112
definition and start, 112
retention, 114
thread basics, 107
thread interruptions, 108
uncaught exceptions during thread execu‐
tion, 110
AsyncQueryHandler class, 209, 212–218
asynchronous access to ContentProviders,
212
example, expanding contact list, 214
easy access to ContentProviders, 247
limitations, 218
methods wrapping provider operations of
ContentResolver, 213
AsyncTask class, 157–175, 226, 245
alternatives to, 173
background tasks needing a Looper, 174
local Service, 174
using execute(Runnable), 174
when AsyncTask is trivially implemented,
173
background task execution, 167
application global execution, 169
custom execution, 172
execution across platform versions, 170
cancellation of the task, 161
creating and starting implementations, 160
data passing from execute() to doInBack‐
ground(), 158

252

|

Index

example, downloading images, 164
example, limiting execution to one task at a
time, 163
execute() method, triggering callback to
doInBackground(), 157
executing task in background and delivering
result to UI thread, 158
implementing, 163
Services and, 198
states, 162
AsyncTask.getStatus() method, 162
AsyncTask.isCancelled, 161
AsyncTaskLoader class, 220, 225
extension by custom loaders, 234
atomic regions, 19

B

background processes, 7
background task execution (AsyncTask), 167
application global execution, 169
custom execution, 172
execution across platform versions, 170
background tasks, in Service restarts, 186
background threads, 30
binder class, 76
Binder class
ServiceBinder subclass (example), 194
binder framework, 75
message passing with, 83
one-way communication, 84
two-way communication, 86
binder threads, 30, 91
blocked threads, 20, 108
causing memory leaks, 93
BlockingQueue class, 46
Bluetooth connection (user-controlled Service
example), 187
bound services, 5, 182, 192
local binding, 194
bounded task queues, 140
BroadcastReceiver, 5
asynchronous execution in, 204–207
example, periodical long operations, 205
use in content management, 236
BroadcastReceiver.goAsync() method, 204
BroadcastReceiver.PendingResult, 205

C

cached thread pools, 137
Callable interface, 146
retrieving a result, 148
CalledFromWrongThreadException, 29
cancellation points, 109
cancellation requests to AsyncTask, 161
CancellationSignal, 218
CFS (completely fair scheduler), 34
cgroups (see control groups)
completely fair scheduler (CFS), 34
components, 3
independence of thread lifecycle from, 177
lifecycle mismatch between components, ob‐
jects, and threads, 96
Service, creation of, 182
concurrent execution of tasks, 26
AsyncTask implementations, 167
across platform versions, 170
concurrent file download executor running
in a Service, 190
consumer-producer pattern (example), 24
ContentObserver, 236
ContentProvider, 5, 209–218
asynchronous access with AsyncQueryHan‐
dler, 212–218
examplel, expanding a contact list, 214
easy access to, 247
introduction to, 209
justification for background processing of,
211
ContentProviderOperation class, 218
ContentResolver class, 210, 217
AsyncQueryHandler methods wrapping
provider operations, 213
context switch, 16
Context.bindService() method, 5, 182, 192
Context.startService() method, 5, 182
Context.stopService() method, 5, 185
Context.unbindService() method, 193
control groups (thread), 36
core threads, 140
critical sections (of code), 20
CursorLoader class, 220, 226
adding CRUD support, 229
reader of provider data, 247
using, 227
example, contact list, 227

D

Dalvik Debug Monitor Service (DDMS), 33
Dalvik runtime, 2
data inconsistency in multithreaded applica‐
tions, 18
data messages
processing, 66
sending, memory leaks from, 98
DDMS (Dalvik Debug Monitor Service), 33
definition and start, threads, 112
defining a thread as a public class, 112
defining a thread as a static inner class, 113
summary of options for thread definition,
113
using anonymous inner class, 112
dispatch time for messages, 62
dispatched state (messages), 57
DownloadService (example), 191
dynamic-size thread pools, 137, 139

E

empty processes, 7
exceptions, uncaught, during thead execution,
110
ExecutionException, 147
Executor framework, 133–155
custom Executor for AsyncTask implemen‐
tations, 172
execution behaviors controlled by, 134
Executor interface, 133
simple implementation, 134
ExecutorCompletionService, 152
SerialExecutor implementation (example),
134
task management, 146
rejecting tasks, 151
submitting tasks, 147
task representation, 146
thread pools, 244
advantages of using, 136
custom, 137
designing, 138
predefined, 136
use cases and pitfalls, 145
ExecutorCompletionService, 152
Executors class, 136
Executors.newCachedThreadPool() method,
137

Index

|

253

Executors.newFixedThreadPool() method, 136,
141
Executors.newSingleThreadExecutor() method,
137
ExecutorService interface, 147
ExecutorService.invokeAll() method, 149
ExecutorService.invokeAny() method, 151
explicit intents, 3
explicit locks, 23

F

FileLoader class (example), 238
FileObserver class, 236, 239
fixed-size thread pools, 136
foreground processes, 7
Fragment
FileListFragment (example), 240
loaders working with, 220
retaining a thread in, 117
Future interface, 147

G

garbage collection, 89
garbage collection roots, 90
stopped by retained threads, 116
thread lifecycle and its GC roots, 92
garbage collector (GC), 89
global services, 180
grep command, 32

H

Handler class, 48, 60–68
inserting messages in message queue, 62
message creation, 61
message processing, 66
methods for managing the message queue,
69
removing messages from the queue, 68
setup of handlers, 60
using Handler and Runnable in thread com‐
munication, memory leaks from, 98
using with Messenger, 87
HandlerThread, 121–131, 245
background task executor in IntentService,
199
fundamentals, 121
lifecycle, 123

254

|

Index

limiting access to, 122
use cases, 124
conditional task insertion, 131
data persistence with SharedPreferences,
125
related tasks, 125
repeated task execution, 125
task chaining, 128
using instead of AsyncTask, 174
heap, 44, 89
HTTP requests, 201

I

IBinder interface, 193
IBinder.FLAG_ONEWAY, 77
idle time for threads in thead pool, 138
IdleHandler interface, 52
using to terminate an unused thread, 53
implicit intents, 3
inner classes, 93
anonymous, 112
static, 94, 113
using to avoid memory leaks, 101
IntentFilter, 3, 181
Intents, 3, 179
identifying Service to bind to, 193
terminated Services and, 185
IntentService class, 199–208, 246
client creating start request, 200
good ways to use, 201
asynchronous execution in BroadcastRe‐
ceiver, 204–207
sequentially ordered tasks, 201
overriding with application specific imple‐
mentation, 200
restoring IntentService if process is killed,
200
Service class versus, 207
interfaces
asynchronous RPC, 82
remote communication, construction of, 77
interprocess communication (IPC), 75–87
Android Interface Definition Language
(AIDL), 77
asynchronous RPC, 81
synchronous RPC, 79
Android RPC, 75
binder class, 76