Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 1 | page.title=Processes and Threads |
Joe Fernandez | 33baa5a | 2013-11-14 11:41:19 -0800 | [diff] [blame] | 2 | page.tags=lifecycle,background |
Scott Main | 64461bf | 2013-04-11 19:32:08 -0700 | [diff] [blame] | 3 | |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 4 | @jd:body |
| 5 | |
| 6 | <div id="qv-wrapper"> |
| 7 | <div id="qv"> |
| 8 | <h2>Quickview</h2> |
| 9 | <ul> |
| 10 | <li>Every application runs in its own process and all components of the application run in that |
| 11 | process, by default</li> |
| 12 | <li>Any slow, blocking operations in an activity should be done in a new thread, to avoid slowing |
| 13 | down the user interface</li> |
| 14 | </ul> |
| 15 | |
| 16 | <h2>In this document</h2> |
| 17 | <ol> |
| 18 | <li><a href="#Processes">Processes</a> |
| 19 | <ol> |
| 20 | <li><a href="#Lifecycle">Process lifecycle</a></li> |
| 21 | </ol> |
| 22 | </li> |
| 23 | <li><a href="#Threads">Threads</a> |
| 24 | <ol> |
| 25 | <li><a href="#WorkerThreads">Worker threads</a></li> |
| 26 | <li><a href="#ThreadSafe">Thread-safe methods</a></li> |
| 27 | </ol> |
| 28 | </li> |
| 29 | <li><a href="#IPC">Interprocess Communication</a></li> |
| 30 | </ol> |
| 31 | |
| 32 | </div> |
| 33 | </div> |
| 34 | |
| 35 | <p>When an application component starts and the application does not have any other components |
| 36 | running, the Android system starts a new Linux process for the application with a single thread of |
| 37 | execution. By default, all components of the same application run in the same process and thread |
| 38 | (called the "main" thread). If an application component starts and there already exists a process |
| 39 | for that application (because another component from the application exists), then the component is |
| 40 | started within that process and uses the same thread of execution. However, you can arrange for |
| 41 | different components in your application to run in separate processes, and you can create additional |
| 42 | threads for any process.</p> |
| 43 | |
| 44 | <p>This document discusses how processes and threads work in an Android application.</p> |
| 45 | |
| 46 | |
| 47 | <h2 id="Processes">Processes</h2> |
| 48 | |
| 49 | <p>By default, all components of the same application run in the same process and most applications |
| 50 | should not change this. However, if you find that you need to control which process a certain |
| 51 | component belongs to, you can do so in the manifest file.</p> |
| 52 | |
| 53 | <p>The manifest entry for each type of component element—<a |
| 54 | href="{@docRoot}guide/topics/manifest/activity-element.html">{@code |
| 55 | <activity>}</a>, <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code |
| 56 | <service>}</a>, <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code |
| 57 | <receiver>}</a>, and <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code |
| 58 | <provider>}</a>—supports an {@code android:process} attribute that can specify a |
| 59 | process in which that component should run. You can set this attribute so that each component runs |
| 60 | in its own process or so that some components share a process while others do not. You can also set |
| 61 | {@code android:process} so that components of different applications run in the same |
| 62 | process—provided that the applications share the same Linux user ID and are signed with the |
| 63 | same certificates.</p> |
| 64 | |
| 65 | <p>The <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code |
| 66 | <application>}</a> element also supports an {@code android:process} attribute, to set a |
| 67 | default value that applies to all components.</p> |
| 68 | |
| 69 | <p>Android might decide to shut down a process at some point, when memory is low and required by |
| 70 | other processes that are more immediately serving the user. Application |
| 71 | components running in the process that's killed are consequently destroyed. A process is started |
| 72 | again for those components when there's again work for them to do.</p> |
| 73 | |
| 74 | <p>When deciding which processes to kill, the Android system weighs their relative importance to |
| 75 | the user. For example, it more readily shuts down a process hosting activities that are no longer |
| 76 | visible on screen, compared to a process hosting visible activities. The decision whether to |
| 77 | terminate a process, therefore, depends on the state of the components running in that process. The |
| 78 | rules used to decide which processes to terminate is discussed below. </p> |
| 79 | |
| 80 | |
| 81 | <h3 id="Lifecycle">Process lifecycle</h3> |
| 82 | |
| 83 | <p>The Android system tries to maintain an application process for as long as possible, but |
| 84 | eventually needs to remove old processes to reclaim memory for new or more important processes. To |
| 85 | determine which processes to keep |
| 86 | and which to kill, the system places each process into an "importance hierarchy" based on the |
| 87 | components running in the process and the state of those components. Processes with the lowest |
| 88 | importance are eliminated first, then those with the next lowest importance, and so on, as necessary |
| 89 | to recover system resources.</p> |
| 90 | |
| 91 | <p>There are five levels in the importance hierarchy. The following list presents the different |
| 92 | types of processes in order of importance (the first process is <em>most important</em> and is |
| 93 | <em>killed last</em>):</p> |
| 94 | |
| 95 | <ol> |
| 96 | <li><b>Foreground process</b> |
| 97 | <p>A process that is required for what the user is currently doing. A |
| 98 | process is considered to be in the foreground if any of the following conditions are true:</p> |
| 99 | |
| 100 | <ul> |
| 101 | <li>It hosts an {@link android.app.Activity} that the user is interacting with (the {@link |
| 102 | android.app.Activity}'s {@link android.app.Activity#onResume onResume()} method has been |
| 103 | called).</li> |
| 104 | |
| 105 | <li>It hosts a {@link android.app.Service} that's bound to the activity that the user is |
| 106 | interacting with.</li> |
| 107 | |
| 108 | <li>It hosts a {@link android.app.Service} that's running "in the foreground"—the |
| 109 | service has called {@link android.app.Service#startForeground startForeground()}. |
| 110 | |
| 111 | <li>It hosts a {@link android.app.Service} that's executing one of its lifecycle |
| 112 | callbacks ({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart |
| 113 | onStart()}, or {@link android.app.Service#onDestroy onDestroy()}).</li> |
| 114 | |
| 115 | <li>It hosts a {@link android.content.BroadcastReceiver} that's executing its {@link |
| 116 | android.content.BroadcastReceiver#onReceive onReceive()} method.</li> |
| 117 | </ul> |
| 118 | |
| 119 | <p>Generally, only a few foreground processes exist at any given time. They are killed only as |
| 120 | a last resort—if memory is so low that they cannot all continue to run. Generally, at that |
| 121 | point, the device has reached a memory paging state, so killing some foreground processes is |
| 122 | required to keep the user interface responsive.</p></li> |
| 123 | |
| 124 | <li><b>Visible process</b> |
| 125 | <p>A process that doesn't have any foreground components, but still can |
| 126 | affect what the user sees on screen. A process is considered to be visible if either of the |
| 127 | following conditions are true:</p> |
| 128 | |
| 129 | <ul> |
| 130 | <li>It hosts an {@link android.app.Activity} that is not in the foreground, but is still |
| 131 | visible to the user (its {@link android.app.Activity#onPause onPause()} method has been called). |
| 132 | This might occur, for example, if the foreground activity started a dialog, which allows the |
| 133 | previous activity to be seen behind it.</li> |
| 134 | |
| 135 | <li>It hosts a {@link android.app.Service} that's bound to a visible (or foreground) |
| 136 | activity.</li> |
| 137 | </ul> |
| 138 | |
| 139 | <p>A visible process is considered extremely important and will not be killed unless doing so |
| 140 | is required to keep all foreground processes running. </p> |
| 141 | </li> |
| 142 | |
| 143 | <li><b>Service process</b> |
| 144 | <p>A process that is running a service that has been started with the {@link |
| 145 | android.content.Context#startService startService()} method and does not fall into either of the two |
| 146 | higher categories. Although service processes are not directly tied to anything the user sees, they |
| 147 | are generally doing things that the user cares about (such as playing music in the background or |
| 148 | downloading data on the network), so the system keeps them running unless there's not enough memory |
| 149 | to retain them along with all foreground and visible processes. </p> |
| 150 | </li> |
| 151 | |
| 152 | <li><b>Background process</b> |
| 153 | <p>A process holding an activity that's not currently visible to the user (the activity's |
| 154 | {@link android.app.Activity#onStop onStop()} method has been called). These processes have no direct |
| 155 | impact on the user experience, and the system can kill them at any time to reclaim memory for a |
| 156 | foreground, |
| 157 | visible, or service process. Usually there are many background processes running, so they are kept |
| 158 | in an LRU (least recently used) list to ensure that the process with the activity that was most |
| 159 | recently seen by the user is the last to be killed. If an activity implements its lifecycle methods |
| 160 | correctly, and saves its current state, killing its process will not have a visible effect on |
| 161 | the user experience, because when the user navigates back to the activity, the activity restores |
| 162 | all of its visible state. See the <a |
| 163 | href="{@docRoot}guide/components/activities.html#SavingActivityState">Activities</a> |
| 164 | document for information about saving and restoring state.</p> |
| 165 | </li> |
| 166 | |
| 167 | <li><b>Empty process</b> |
| 168 | <p>A process that doesn't hold any active application components. The only reason to keep this |
| 169 | kind of process alive is for caching purposes, to improve startup time the next time a component |
| 170 | needs to run in it. The system often kills these processes in order to balance overall system |
| 171 | resources between process caches and the underlying kernel caches.</p> |
| 172 | </li> |
| 173 | </ol> |
| 174 | |
| 175 | |
| 176 | <p>Android ranks a process at the highest level it can, based upon the importance of the |
| 177 | components currently active in the process. For example, if a process hosts a service and a visible |
| 178 | activity, the process is ranked as a visible process, not a service process.</p> |
| 179 | |
| 180 | <p>In addition, a process's ranking might be increased because other processes are dependent on |
| 181 | it—a process that is serving another process can never be ranked lower than the process it is |
| 182 | serving. For example, if a content provider in process A is serving a client in process B, or if a |
| 183 | service in process A is bound to a component in process B, process A is always considered at least |
| 184 | as important as process B.</p> |
| 185 | |
| 186 | <p>Because a process running a service is ranked higher than a process with background activities, |
| 187 | an activity that initiates a long-running operation might do well to start a <a |
| 188 | href="{@docRoot}guide/components/services.html">service</a> for that operation, rather than |
| 189 | simply create a worker thread—particularly if the operation will likely outlast the activity. |
| 190 | For example, an activity that's uploading a picture to a web site should start a service to perform |
| 191 | the upload so that the upload can continue in the background even if the user leaves the activity. |
| 192 | Using a service guarantees that the operation will have at least "service process" priority, |
| 193 | regardless of what happens to the activity. This is the same reason that broadcast receivers should |
| 194 | employ services rather than simply put time-consuming operations in a thread.</p> |
| 195 | |
| 196 | |
| 197 | |
| 198 | |
| 199 | <h2 id="Threads">Threads</h2> |
| 200 | |
| 201 | <p>When an application is launched, the system creates a thread of execution for the application, |
| 202 | called "main." This thread is very important because it is in charge of dispatching events to |
| 203 | the appropriate user interface widgets, including drawing events. It is also the thread in which |
| 204 | your application interacts with components from the Android UI toolkit (components from the {@link |
| 205 | android.widget} and {@link android.view} packages). As such, the main thread is also sometimes |
| 206 | called the UI thread.</p> |
| 207 | |
| 208 | <p>The system does <em>not</em> create a separate thread for each instance of a component. All |
| 209 | components that run in the same process are instantiated in the UI thread, and system calls to |
| 210 | each component are dispatched from that thread. Consequently, methods that respond to system |
| 211 | callbacks (such as {@link android.view.View#onKeyDown onKeyDown()} to report user actions |
| 212 | or a lifecycle callback method) always run in the UI thread of the process.</p> |
| 213 | |
| 214 | <p>For instance, when the user touches a button on the screen, your app's UI thread dispatches the |
| 215 | touch event to the widget, which in turn sets its pressed state and posts an invalidate request to |
| 216 | the event queue. The UI thread dequeues the request and notifies the widget that it should redraw |
| 217 | itself.</p> |
| 218 | |
| 219 | <p>When your app performs intensive work in response to user interaction, this single thread model |
| 220 | can yield poor performance unless you implement your application properly. Specifically, if |
| 221 | everything is happening in the UI thread, performing long operations such as network access or |
| 222 | database queries will block the whole UI. When the thread is blocked, no events can be dispatched, |
| 223 | including drawing events. From the user's perspective, the |
| 224 | application appears to hang. Even worse, if the UI thread is blocked for more than a few seconds |
| 225 | (about 5 seconds currently) the user is presented with the infamous "<a |
| 226 | href="http://developer.android.com/guide/practices/responsiveness.html">application not |
| 227 | responding</a>" (ANR) dialog. The user might then decide to quit your application and uninstall it |
| 228 | if they are unhappy.</p> |
| 229 | |
| 230 | <p>Additionally, the Andoid UI toolkit is <em>not</em> thread-safe. So, you must not manipulate |
| 231 | your UI from a worker thread—you must do all manipulation to your user interface from the UI |
| 232 | thread. Thus, there are simply two rules to Android's single thread model:</p> |
| 233 | |
| 234 | <ol> |
| 235 | <li>Do not block the UI thread |
| 236 | <li>Do not access the Android UI toolkit from outside the UI thread |
| 237 | </ol> |
| 238 | |
| 239 | <h3 id="WorkerThreads">Worker threads</h3> |
| 240 | |
| 241 | <p>Because of the single thread model described above, it's vital to the responsiveness of your |
| 242 | application's UI that you do not block the UI thread. If you have operations to perform |
| 243 | that are not instantaneous, you should make sure to do them in separate threads ("background" or |
| 244 | "worker" threads).</p> |
| 245 | |
| 246 | <p>For example, below is some code for a click listener that downloads an image from a separate |
| 247 | thread and displays it in an {@link android.widget.ImageView}:</p> |
| 248 | |
| 249 | <pre> |
| 250 | public void onClick(View v) { |
| 251 | new Thread(new Runnable() { |
| 252 | public void run() { |
| 253 | Bitmap b = loadImageFromNetwork("http://example.com/image.png"); |
| 254 | mImageView.setImageBitmap(b); |
| 255 | } |
| 256 | }).start(); |
| 257 | } |
| 258 | </pre> |
| 259 | |
| 260 | <p>At first, this seems to work fine, because it creates a new thread to handle the network |
| 261 | operation. However, it violates the second rule of the single-threaded model: <em>do not access the |
| 262 | Android UI toolkit from outside the UI thread</em>—this sample modifies the {@link |
| 263 | android.widget.ImageView} from the worker thread instead of the UI thread. This can result in |
| 264 | undefined and unexpected behavior, which can be difficult and time-consuming to track down.</p> |
| 265 | |
| 266 | <p>To fix this problem, Android offers several ways to access the UI thread from other |
| 267 | threads. Here is a list of methods that can help:</p> |
| 268 | |
| 269 | <ul> |
| 270 | <li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable) |
| 271 | Activity.runOnUiThread(Runnable)}</li> |
| 272 | <li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li> |
| 273 | <li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, |
| 274 | long)}</li> |
| 275 | </ul> |
| 276 | |
| 277 | <p>For example, you can fix the above code by using the {@link |
| 278 | android.view.View#post(java.lang.Runnable) View.post(Runnable)} method:</p> |
| 279 | |
| 280 | <pre> |
| 281 | public void onClick(View v) { |
| 282 | new Thread(new Runnable() { |
| 283 | public void run() { |
| 284 | final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); |
| 285 | mImageView.post(new Runnable() { |
| 286 | public void run() { |
| 287 | mImageView.setImageBitmap(bitmap); |
| 288 | } |
| 289 | }); |
| 290 | } |
| 291 | }).start(); |
| 292 | } |
| 293 | </pre> |
| 294 | |
| 295 | <p>Now this implementation is thread-safe: the network operation is done from a separate thread |
| 296 | while the {@link android.widget.ImageView} is manipulated from the UI thread.</p> |
| 297 | |
| 298 | <p>However, as the complexity of the operation grows, this kind of code can get complicated and |
| 299 | difficult to maintain. To handle more complex interactions with a worker thread, you might consider |
| 300 | using a {@link android.os.Handler} in your worker thread, to process messages delivered from the UI |
| 301 | thread. Perhaps the best solution, though, is to extend the {@link android.os.AsyncTask} class, |
| 302 | which simplifies the execution of worker thread tasks that need to interact with the UI.</p> |
| 303 | |
| 304 | |
| 305 | <h4 id="AsyncTask">Using AsyncTask</h4> |
| 306 | |
| 307 | <p>{@link android.os.AsyncTask} allows you to perform asynchronous work on your user |
| 308 | interface. It performs the blocking operations in a worker thread and then publishes the results on |
| 309 | the UI thread, without requiring you to handle threads and/or handlers yourself.</p> |
| 310 | |
| 311 | <p>To use it, you must subclass {@link android.os.AsyncTask} and implement the {@link |
| 312 | android.os.AsyncTask#doInBackground doInBackground()} callback method, which runs in a pool of |
| 313 | background threads. To update your UI, you should implement {@link |
| 314 | android.os.AsyncTask#onPostExecute onPostExecute()}, which delivers the result from {@link |
| 315 | android.os.AsyncTask#doInBackground doInBackground()} and runs in the UI thread, so you can safely |
| 316 | update your UI. You can then run the task by calling {@link android.os.AsyncTask#execute execute()} |
| 317 | from the UI thread.</p> |
| 318 | |
| 319 | <p>For example, you can implement the previous example using {@link android.os.AsyncTask} this |
| 320 | way:</p> |
| 321 | |
| 322 | <pre> |
| 323 | public void onClick(View v) { |
| 324 | new DownloadImageTask().execute("http://example.com/image.png"); |
| 325 | } |
| 326 | |
| 327 | private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { |
| 328 | /** The system calls this to perform work in a worker thread and |
| 329 | * delivers it the parameters given to AsyncTask.execute() */ |
| 330 | protected Bitmap doInBackground(String... urls) { |
| 331 | return loadImageFromNetwork(urls[0]); |
| 332 | } |
| 333 | |
| 334 | /** The system calls this to perform work in the UI thread and delivers |
| 335 | * the result from doInBackground() */ |
| 336 | protected void onPostExecute(Bitmap result) { |
| 337 | mImageView.setImageBitmap(result); |
| 338 | } |
| 339 | } |
| 340 | </pre> |
| 341 | |
| 342 | <p>Now the UI is safe and the code is simpler, because it separates the work into the |
| 343 | part that should be done on a worker thread and the part that should be done on the UI thread.</p> |
| 344 | |
| 345 | <p>You should read the {@link android.os.AsyncTask} reference for a full understanding on |
| 346 | how to use this class, but here is a quick overview of how it works:</p> |
| 347 | |
| 348 | <ul> |
| 349 | <li>You can specify the type of the parameters, the progress values, and the final |
| 350 | value of the task, using generics</li> |
| 351 | <li>The method {@link android.os.AsyncTask#doInBackground doInBackground()} executes automatically |
| 352 | on a worker thread</li> |
| 353 | <li>{@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link |
| 354 | android.os.AsyncTask#onPostExecute onPostExecute()}, and {@link |
| 355 | android.os.AsyncTask#onProgressUpdate onProgressUpdate()} are all invoked on the UI thread</li> |
| 356 | <li>The value returned by {@link android.os.AsyncTask#doInBackground doInBackground()} is sent to |
| 357 | {@link android.os.AsyncTask#onPostExecute onPostExecute()}</li> |
| 358 | <li>You can call {@link android.os.AsyncTask#publishProgress publishProgress()} at anytime in {@link |
| 359 | android.os.AsyncTask#doInBackground doInBackground()} to execute {@link |
| 360 | android.os.AsyncTask#onProgressUpdate onProgressUpdate()} on the UI thread</li> |
| 361 | <li>You can cancel the task at any time, from any thread</li> |
| 362 | </ul> |
| 363 | |
| 364 | <p class="caution"><strong>Caution:</strong> Another problem you might encounter when using a worker |
| 365 | thread is unexpected restarts in your activity due to a <a |
| 366 | href="{@docRoot}guide/topics/resources/runtime-changes.html">runtime configuration change</a> |
| 367 | (such as when the user changes the screen orientation), which may destroy your worker thread. To |
| 368 | see how you can persist your task during one of these restarts and how to properly cancel the task |
| 369 | when the activity is destroyed, see the source code for the <a |
| 370 | href="http://code.google.com/p/shelves/">Shelves</a> sample application.</p> |
| 371 | |
| 372 | |
| 373 | <h3 id="ThreadSafe">Thread-safe methods</h3> |
| 374 | |
| 375 | <p> In some situations, the methods you implement might be called from more than one thread, and |
| 376 | therefore must be written to be thread-safe. </p> |
| 377 | |
| 378 | <p>This is primarily true for methods that can be called remotely—such as methods in a <a |
| 379 | href="{@docRoot}guide/components/bound-services.html">bound service</a>. When a call on a |
| 380 | method implemented in an {@link android.os.IBinder} originates in the same process in which the |
| 381 | {@link android.os.IBinder IBinder} is running, the method is executed in the caller's thread. |
| 382 | However, when the call originates in another process, the method is executed in a thread chosen from |
| 383 | a pool of threads that the system maintains in the same process as the {@link android.os.IBinder |
| 384 | IBinder} (it's not executed in the UI thread of the process). For example, whereas a service's |
| 385 | {@link android.app.Service#onBind onBind()} method would be called from the UI thread of the |
| 386 | service's process, methods implemented in the object that {@link android.app.Service#onBind |
| 387 | onBind()} returns (for example, a subclass that implements RPC methods) would be called from threads |
| 388 | in the pool. Because a service can have more than one client, more than one pool thread can engage |
| 389 | the same {@link android.os.IBinder IBinder} method at the same time. {@link android.os.IBinder |
| 390 | IBinder} methods must, therefore, be implemented to be thread-safe.</p> |
| 391 | |
| 392 | <p> Similarly, a content provider can receive data requests that originate in other processes. |
| 393 | Although the {@link android.content.ContentResolver} and {@link android.content.ContentProvider} |
| 394 | classes hide the details of how the interprocess communication is managed, {@link |
| 395 | android.content.ContentProvider} methods that respond to those requests—the methods {@link |
| 396 | android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert |
| 397 | insert()}, {@link android.content.ContentProvider#delete delete()}, {@link |
| 398 | android.content.ContentProvider#update update()}, and {@link android.content.ContentProvider#getType |
| 399 | getType()}—are called from a pool of threads in the content provider's process, not the UI |
| 400 | thread for the process. Because these methods might be called from any number of threads at the |
| 401 | same time, they too must be implemented to be thread-safe. </p> |
| 402 | |
| 403 | |
| 404 | <h2 id="IPC">Interprocess Communication</h2> |
| 405 | |
| 406 | <p>Android offers a mechanism for interprocess communication (IPC) using remote procedure calls |
| 407 | (RPCs), in which a method is called by an activity or other application component, but executed |
| 408 | remotely (in another process), with any result returned back to the |
| 409 | caller. This entails decomposing a method call and its data to a level the operating system can |
| 410 | understand, transmitting it from the local process and address space to the remote process and |
| 411 | address space, then reassembling and reenacting the call there. Return values are then |
| 412 | transmitted in the opposite direction. Android provides all the code to perform these IPC |
| 413 | transactions, so you can focus on defining and implementing the RPC programming interface. </p> |
| 414 | |
| 415 | <p>To perform IPC, your application must bind to a service, using {@link |
| 416 | android.content.Context#bindService bindService()}. For more information, see the <a |
| 417 | href="{@docRoot}guide/components/services.html">Services</a> developer guide.</p> |
| 418 | |
| 419 | |
| 420 | <!-- |
| 421 | <h2>Beginner's Path</h2> |
| 422 | |
| 423 | <p>For information about how to perform work in the background for an indefinite period of time |
| 424 | (without a user interface), continue with the <b><a |
| 425 | href="{@docRoot}guide/components/services.html">Services</a></b> document.</p> |
| 426 | --> |