除非另行指定 , 大部分应用程序操作在前台称作为UI 线程的特殊线程中运行 。 这可能会造成问题 , 因为 长时间运行的操作 会影响 你的用户界面的 响应 。 这 干扰 用户交互操作 , 甚至可能导致 系统错误 。 为了避免这种情况, Android框架 提供了 一些类, 帮助您 关闭负载 操作 放在一个独立运行于后台的 线程 。 最有用的 是 intentservice 。
接下来将描述如下三种操作。
1. 创建一个后台服务这IntentServic类给单个后台线程运行某个操作提供了一个直接了断的结构, 允许它处理长时间运行的操作,不影响用户界面的响应,同时,一个IntentService 也不会被多用户界面生命周期事件的影响,因此它继续运行的情况下,将关闭AsyncTask。
一个IntentService 有一些限制:
- 不能和用户直接交互,为将结果更新UI, 必须发送结果给Activity。
- 工作请求是有序的,如果一个操作正在IntentService中运行
,而且发送另一个请求给IntentService, 这个请求需要等待知道第一个请求操作完成.
- 运行在IntentService中的操作,不能够被中断.
然而,在大多数情况下,Intentservice 是简单的后台操作的首选方法。
如何创建您自己的IntentService子类以及创建请求回调方法onHandleIntent(),最后描述如何在Manifest 文件中定义IntentService。
a。创建一个IntentService
为创建一个IntentService 组件,定义一个继承于IntentService的类, 并且在该类中实现onHandleIntent()方法。
例子如下:
/**
* This service pulls RSS content from a web site URL contained in the incoming Intent (see
* onHandleIntent()). As it runs, it broadcasts its status using LocalBroadcastManager; any
* component that wants to see the status should implement a subclass of BroadcastReceiver and
* register to receive broadcast Intents with category = CATEGORY_DEFAULT and action
* Constants.BROADCAST_ACTION.
*
*/
public class RSSPullService extends IntentService {
// Used to write to the system log from this class.
public static final String LOG_TAG = "RSSPullService";
// Defines and instantiates an object for handling status updates.
private BroadcastNotifier mBroadcaster = new BroadcastNotifier(this);
/**
* An IntentService must always have a constructor that calls the super constructor. The
* string supplied to the super constructor is used to give a name to the IntentService's
* background thread.
*/
public RSSPullService() {
super("RSSPullService");
}
/**
* In an IntentService, onHandleIntent is run on a background thread. As it
* runs, it broadcasts its current status using the LocalBroadcastManager.
* @param workIntent The Intent that starts the IntentService. This Intent contains the
* URL of the web site from which the RSS parser gets data.
*/
@Override
protected void onHandleIntent(Intent workIntent) {
// Gets a URL to read from the incoming Intent's "data" value
String localUrlString = workIntent.getDataString();
// Creates a projection to use in querying the modification date table in the provider.
final String[] dateProjection = new String[]
{
DataProviderContract.ROW_ID,
DataProviderContract.DATA_DATE_COLUMN
};
// A URL that's local to this method
URL localURL;
// A cursor that's local to this method.
Cursor cursor = null;
/*
* A block that tries to connect to the Picasa featured picture URL passed as the "data"
* value in the incoming Intent. The block throws exceptions (see the end of the block).
*/
try {
// Convert the incoming data string to a URL.
localURL = new URL(localUrlString);
/*
* Tries to open a connection to the URL. If an IO error occurs, this throws an
* IOException
*/
URLConnection localURLConnection = localURL.openConnection();
// If the connection is an HTTP connection, continue
if ((localURLConnection instanceof HttpURLConnection)) {
// Broadcasts an Intent indicating that processing has started.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);
// Casts the connection to a HTTP connection
HttpURLConnection localHttpURLConnection = (HttpURLConnection) localURLConnection;
// Sets the user agent for this request.
localHttpURLConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
/*
* Queries the content provider to see if this URL was read previously, and when.
* The content provider throws an exception if the URI is invalid.
*/
cursor = getContentResolver().query(
DataProviderContract.DATE_TABLE_CONTENTURI,
dateProjection,
null,
null,
null);
// Flag to indicate that new metadata was retrieved
boolean newMetadataRetrieved;
/*
* Tests to see if the table contains a modification date for the URL
*/
if (null != cursor && cursor.moveToFirst()) {
// Find the URL's last modified date in the content provider
long storedModifiedDate =
cursor.getLong(cursor.getColumnIndex(
DataProviderContract.DATA_DATE_COLUMN)
)
;
/*
* If the modified date isn't 0, sets another request property to ensure that
* data is only downloaded if it has changed since the last recorded
* modification date. Formats the date according to the RFC1123 format.
*/
if (0 != storedModifiedDate) {
localHttpURLConnection.setRequestProperty(
"If-Modified-Since",
org.apache.http.impl.cookie.DateUtils.formatDate(
new Date(storedModifiedDate),
org.apache.http.impl.cookie.DateUtils.PATTERN_RFC1123));
}
// Marks that new metadata does not need to be retrieved
newMetadataRetrieved = false;
} else {
/*
* No modification date was found for the URL, so newmetadata has to be
* retrieved.
*/
newMetadataRetrieved = true;
}
// Reports that the service is about to connect to the RSS feed
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_CONNECTING);
// Gets a response code from the RSS server
int responseCode = localHttpURLConnection.getResponseCode();
switch (responseCode) {
// If the response is OK
case HttpStatus.SC_OK:
// Gets the last modified data for the URL
long lastModifiedDate = localHttpURLConnection.getLastModified();
// Reports that the service is parsing
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_PARSING);
/*
* Instantiates a pull parser and uses it to parse XML from the RSS feed.
* The mBroadcaster argument send a broadcaster utility object to the
* parser.
*/
RSSPullParser localPicasaPullParser = new RSSPullParser();
localPicasaPullParser.parseXml(
localURLConnection.getInputStream(),
mBroadcaster);
// Reports that the service is now writing data to the content provider.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_WRITING);
// Gets image data from the parser
Vector<ContentValues> imageValues = localPicasaPullParser.getImages();
// Stores the number of images
int imageVectorSize = imageValues.size();
// Creates one ContentValues for each image
ContentValues[] imageValuesArray = new ContentValues[imageVectorSize];
imageValuesArray = imageValues.toArray(imageValuesArray);
/*
* Stores the image data in the content provider. The content provider
* throws an exception if the URI is invalid.
*/
getContentResolver().bulkInsert(
DataProviderContract.PICTUREURL_TABLE_CONTENTURI, imageValuesArray);
// Creates another ContentValues for storing date information
ContentValues dateValues = new ContentValues();
// Adds the URL's last modified date to the ContentValues
dateValues.put(DataProviderContract.DATA_DATE_COLUMN, lastModifiedDate);
if (newMetadataRetrieved) {
// No previous metadata existed, so insert the data
getContentResolver().insert(
DataProviderContract.DATE_TABLE_CONTENTURI,
dateValues
);
} else {
// Previous metadata existed, so update it.
getContentResolver().update(
DataProviderContract.DATE_TABLE_CONTENTURI,
dateValues,
DataProviderContract.ROW_ID + "=" +
cursor.getString(cursor.getColumnIndex(
DataProviderContract.ROW_ID)), null);
}
break;
}
// Reports that the feed retrieval is complete.
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
}
// Handles possible exceptions
} catch (MalformedURLException localMalformedURLException) {
localMalformedURLException.printStackTrace();
} catch (IOException localIOException) {
localIOException.printStackTrace();
} catch (XmlPullParserException localXmlPullParserException) {
localXmlPullParserException.printStackTrace();
} finally {
// If an exception occurred, close the cursor to prevent memory leaks.
if (null != cursor) {
cursor.close();
}
}
}
}
注意: 这其他常规的Service组件的回调方法, 例如onStartCommand() 是自动被IntentServices调用,在一个IntentService中, 应当避免重载那些回调方法。
b.在Manifest 文件中定义IntentService
一个IntentService 也需要在应用manifest 文件中应该一个入口(entry),使用<service>元素提供这个入口service,<service>元素 是<application>元素的子项。具体定义如下:
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
...
<!--
Because android:exported is set to "false",
the service is only available to this app.
-->
<service
android:name=".RSSPullService"
android:exported="false"/>
...
<application/>
android:name 指定IntentService 类的名称,注意这<service>元素不包含intent filter,这Activity发送工作请求给intentService使用明确的Intent,因此没有filter的必要,这也意味这仅仅在同一个应用或其他具有相同user ID 的应用才能访问该服务。发送工作请求给后台服务将在下一篇“android 后台运行服务之发送工作请求给后台服务篇”讲述。