Android Best Practices for Background Jobs
文章目录
Best Practices for Background Jobs
These classes show you how to run jobs in the background to boost your application’s performance and minimize its drain on the battery.
Running in a background service
This class describes how to implement an IntentService, send it work requests, and report its results to other components.
Creating a background service
The IntentService class provides a straightforward structure for running an operation on a single background thread. This allows it to handle long-running operations without affecting your user interface’s responsiveness. Also, an IntentService isn’t affected by most user interface lifecycle events, so it continues to run in circumstances that would shut down an AsyncTask.
An IntentService has a few limitations:
- It can’t interact directly with your user interface. To put its results in the UI, you have to send them to an Activity.不能直接与UI交互
- Work requests run sequentially. If an operation is running in an IntentService, and you send it another request, the request waits until the first operation is finished.任务顺序执行
- An operation running on an IntentService can’t be interrupted.任务不可被打断
However, in most cases an IntentService is the preferred way to simple background operations.
To create an IntentService component for your app, define a class that extends IntentService, and within it, define a method that overrides onHandleIntent()
. For example:
|
|
Notice that the other callbacks of a regular Service component, such as onStartCommand()
are automatically invoked by IntentService. In an IntentService, you should avoid overriding these callbacks.
Sending work requests to the background service
This lesson shows you how to trigger the IntentService to run an operation by sending it an Intent. This Intent can optionally contain data for the IntentService to process. You can send an Intent to an IntentService from any point in an Activity or Fragment.
To create a work request and send it to an IntentService, create an explicit Intent, add work request data to it, and send it to IntentService by calling startService()
.
|
|
Once you call startService()
, the IntentService does the work defined in its onHandleIntent()
method, and then stops itself.
Reporting work status
This lesson shows you how to report the status of a work request run in a background service to the component that sent the request. This allows you, for example, to report the status of the request in an Activity object’s UI. The recommended way to send and receive status is to use a LocalBroadcastManager
, which limits broadcast Intent objects to components in your own app.
To send the status of a work request in an IntentService to other components, first create an Intent that contains the status in its extended data. As an option, you can add an action and data URI to this Intent.
Next, send the Intent by calling LocalBroadcastManager.sendBroadcast()
. This sends the Intent to any component in your application that has registered to receive it. To get an instance of LocalBroadcastManager
, call getInstance()
.
|
|
To receive broadcast Intent objects, use a subclass of BroadcastReceiver
. In the subclass, implement the BroadcastReceiver.onReceive()
callback method, which LocalBroadcastManager
invokes when it receives an Intent. LocalBroadcastManager
passes the incoming Intent to BroadcastReceiver.onReceive()
.
|
|
Sending an broadcast Intent doesn’t start or resume an Activity. The BroadcastReceiver for an Activity receives and processes Intent objects even when your app is in the background, but doesn’t force your app to the foreground. If you want to notify the user about an event that happened in the background while your app was not visible, use a Notification.
Loading data in the background
Besides doing the initial background query, a CursorLoader automatically re-runs the query when data associated with the query changes.This class describes how to use a CursorLoader to run a background query.
Running a query with the CursorLoader
A CursorLoader runs an asynchronous query in the background against a ContentProvider, and returns the results to the Activity or FragmentActivity from which it was called. This allows the Activity or FragmentActivity to continue to interact with the user while the query is ongoing.
|
|
|
|
|
|
Handling the results
The loader then provides the query results to your Activity or FragmentActivity in your implementation of LoaderCallbacks.onLoadFinished(). One of the incoming arguments to this method is a Cursor containing the query results. You can use this object to update your data display or do further processing.
Besides onCreateLoader()
and onLoadFinished()
, you also have to implement onLoaderReset()
. This method is invoked when CursorLoader detects that data associated with the Cursor has changed. When the data changes, the framework also re-runs the current query.
|
|
Managing the device awake state
When an Android device is left idle, it will first dim, then turn off the screen, and ultimately turn off the CPU. This prevents the device’s battery from quickly getting drained. Yet there are times when your application might require a different behavior:
- Apps such as games or movie apps may need to keep the screen turned on.
- Other applications may not need the screen to remain on, but they may require the CPU to keep running until a critical operation finishes.
This class describes how to keep a device awake when necessary without draining its battery.
Keeping the Device Awake
|
|
|
|
Using android:keepScreenOn="true"
is equivalent to using FLAG_KEEP_SCREEN_ON
. You can use whichever approach is best for your app. The advantage of setting the flag programmatically in your activity is that it gives you the option of programmatically clearing the flag later and thereby allowing the screen to turn off.
You don’t need to clear the FLAG_KEEP_SCREEN_ON
flag unless you no longer want the screen to stay on in your running application (for example, if you want the screen to time out after a certain period of inactivity). The window manager takes care of ensuring that the right things happen when the app goes into the background or returns to the foreground. But if you want to explicitly clear the flag and thereby allow the screen to turn off again, use clearFlags()
: getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
.
If you need to keep the CPU running in order to complete some work before the device goes to sleep, you can use a PowerManager
system service feature called wake locks. Wake locks allow your application to control the power state of the host device.
Creating and holding wake locks can have a dramatic impact on the host device’s battery life. Thus you should use wake locks only when strictly necessary and hold them for as short a time as possible. For example, you should never need to use a wake lock in an activity. As described above, if you want to keep the screen on in your activity, use FLAG_KEEP_SCREEN_ON
.
|
|
Scheduling Repeating Alarms
Alarms have these characteristics:
- They let you fire Intents at set times and/or intervals.
- You can use them in conjunction with broadcast receivers to start services and perform other operations.
- They operate outside of your application, so you can use them to trigger events or actions even when your app is not running, and even if the device itself is asleep.
- They help you to minimize your app’s resource requirements. You can schedule operations without relying on timers or continuously running background services.
For timing operations that are guaranteed to occur during the lifetime of your application, instead consider using the Handler class in conjunction with Timer and Thread. This approach gives Android better control over system resources.
Repeating alarms are a good choice for scheduling regular events or data lookups. A repeating alarm has the following characteristics:
- A alarm type.
- A trigger time. If the trigger time you specify is in the past, the alarm triggers immediately.
- The alarm’s interval. For example, once a day, every hour, every 5 seconds, and so on.
- A pending intent that fires when the alarm is triggered. When you set a second alarm that uses the same pending intent, it replaces the original alarm.
Follow these guidelines as you design your app:
- Keep your alarm frequency to a minimum.
- Don’t wake up the device unnecessarily (this behavior is determined by the alarm type, as described in Choose an alarm type).
- Don’t make your alarm’s trigger time any more precise than it has to be:
- Use
setInexactRepeating()
instead ofsetRepeating()
whenever possible. When you usesetInexactRepeating()
, Android synchronizes multiple inexact repeating alarms and fires them at the same time. This reduces the drain on the battery. - If your alarm’s behavior is based on an interval (for example, your alarm fires once an hour) rather than a precise trigger time (for example, your alarm fires at 7 a.m. sharp and every 20 minutes after that), use an
ELAPSED_REALTIME
alarm type.
- Use
|
|
Start an Alarm When the Device Boots
- Set the
RECEIVE_BOOT_COMPLETED
permission in your application’s manifest. This allows your app to receive theACTION_BOOT_COMPLETED
that is broadcast after the system finishes booting (this only works if the app has already been launched by the user at least once):
|
|
- Implement a BroadcastReceiver to receive the broadcast:
|
|
- Add the receiver to your app’s manifest file with an intent filter that filters on the
ACTION_BOOT_COMPLETED
action:
|
|
Notice that in the manifest, the boot receiver is set to android:enabled=“false”
. This means that the receiver will not be called unless the application explicitly enables it. This prevents the boot receiver from being called unnecessarily. You can enable a receiver (for example, if the user sets an alarm) as follows:
|
|
Once you enable the receiver this way, it will stay enabled, even if the user reboots the device. In other words, programmatically enabling the receiver overrides the manifest setting, even across reboots. The receiver will stay enabled until your app disables it. You can disable a receiver (for example, if the user cancels an alarm) as follows:
|
|