Android IntentService解析
在开发安卓应用程序时,除非你指定,否则绝大部分执行动作都运行UI线程中。这种机制会引发一些问题,因为耗时操作会妨碍用户交互行为。这会让用户感到懊恼,甚至引发ANR错误。幸运的是,Android框架提供了一些类,它帮助我们把这些耗时的操作到转移到后台线程中去了。个人觉得最有用的是IntentService类了,但IntentServce也有它的几个局限性:
1. 它不能和用户界面直接交互,你必须把执行的结果发送到Activity中
2. 发送给IntentService的请求是有序的。如果IntentService正在处理任务A,而你又发送了一个任务B请求,此时IntentService只有等到执行完任务A后才会执行任务B。
3. IntentService中正在运行的操作不能被中断。
尽管IntentService有些局限性,但执行简单的后台操作是一个比较好的选择,下面为大家讲述如何使用IntentService。
创建一个IntenService
创建一个IntentService非常简单,只要写一个继承IntentService的类即可,并实现构造方法以及onHandleIntent(Intent workIntent)抽象方法即可。
public class RSSPullService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public RSSPullService () { super("HelloRSSPullService"); } @Override protected void onHandleIntent(Intent workIntent) { // Gets data from the incoming Intent String dataString = workIntent.getDataString(); ... // Do work here, based on the contents of dataString. ... } }
因为onHandleIntent(Intent workIntent)方法运行在后台一个线程中,你可以把耗时的任务转到此处而不必担心它会阻塞UI线程。任务做完后它会自动停止服务。
注册IntentService
仅仅创建了IntentService依然无法使用,你需要在清单文件中去注册它。
<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/>
现在你写的IntentService类就可以使用了,那么怎样使用呢?很简单,你可以通过一个显式意图去启动IntentService,你可以在意图中添加相关的数据以支持你的业务逻辑。
/* * Creates a new Intent to start the RSSPullService * IntentService. Passes a URI in the * Intent's "data" field. */ mServiceIntent = new Intent(getActivity(), RSSPullService.class); mServiceIntent.setData(Uri.parse(dataUrl)); // Starts the IntentService getActivity().startService(mServiceIntent);
一旦你调用了startService(),IntentService就会执行onHandleIntent(),任务结束后服务也就自动停止。
在IntentService中发送广播
那么耗时的任务数据状态通过怎样的形式才能呈现给用户呢?一种方式是你可以通过发送广播来实现。通过广播将任务产生的状态数据发送到广播接收器,在接收器中可以将数据呈现到UI上。
public final class Constants { ... // Defines a custom Intent action public static final String BROADCAST_ACTION = "com.example.android.threadsample.BROADCAST"; ... // Defines the key for the status "extra" in an Intent public static final String EXTENDED_DATA_STATUS = "com.example.android.threadsample.STATUS"; ... } public class RSSPullService extends IntentService { ... /* * Creates a new Intent containing a Uri object * BROADCAST_ACTION is a custom Intent action */ Intent localIntent = new Intent(Constants.BROADCAST_ACTION) // Puts the status into the Intent .putExtra(Constants.EXTENDED_DATA_STATUS, status); // Broadcasts the Intent to receivers in this app. LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); ... }
接受广播发送过来的数据
为了接受广播发送过来的数据,你需要创建一个类,该类继承BroadcastReceiver类,并实现onReceive()方法。
// Broadcast receiver for receiving status updates from the IntentService private class ResponseReceiver extends BroadcastReceiver { // Prevents instantiation private DownloadStateReceiver() { } // Called when the BroadcastReceiver gets an Intent it's registered to receive @ public void onReceive(Context context, Intent intent) { ... /* * Handle Intents here. * you can display data to UI */ ... } }
广播接收器一旦定义好后,你可以定义过滤器以区分动作事件
// Class that displays photos public class DisplayActivity extends FragmentActivity { ... public void onCreate(Bundle stateBundle) { ... super.onCreate(stateBundle); ... // The filter's action is BROADCAST_ACTION IntentFilter mStatusIntentFilter = new IntentFilter( Constants.BROADCAST_ACTION); // Adds a data filter for the HTTP scheme mStatusIntentFilter.addDataScheme("http"); ...
要保证广播接收器能够接受到消息,必须对其进行注册,一般在Activity中onCreate()中对其进行注册,在onDestory()中注销。
// Class that displays photos public class DisplayActivity extends FragmentActivity { ... public void onCreate(Bundle stateBundle) { ... super.onCreate(stateBundle); ... // The filter's action is BROADCAST_ACTION IntentFilter mStatusIntentFilter = new IntentFilter( Constants.BROADCAST_ACTION); // Adds a data filter for the HTTP scheme mStatusIntentFilter.addDataScheme("http"); // Instantiates a new DownloadStateReceiver DownloadStateReceiver mDownloadStateReceiver = new DownloadStateReceiver(); // Registers the DownloadStateReceiver and its intent filters LocalBroadcastManager.getInstance(this).registerReceiver( mDownloadStateReceiver, mStatusIntentFilter); } public void onDestory(){ super.onDestory(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mDownloadStateReceiver); }
LocalBroadcastManager发送的广播只能在程序内被接受因此它能够有效减低信息泄露。