Running in a Background Service
Service相信都很熟悉,Android四大组件之一,后台运行。但这里的后台运行并不是指Service在子线程中运行,而是指Service的运行是不依赖于UI的。Service是在主线程中运行的。
没关系,Google还提供了另外一个IntentService实现后台子线程运行。关于IntentService有几点需要注意的:
- IntentService不能直接跟UI进行交互,必须将结果发送到UI中;
- 请求是按顺序执行的,重复发送请求需要等待之前的执行完毕;
- IntentService中的操作不能被打断。
虽然IntentService局限较多,然而,在大多数情况下,IntentService是执行简单后台操作的首选方法。比如更新Apk,比如一些网络请求等等
创建一个IntentService
public class MyService extends IntentService{
public MyService() {
super("MyService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
String dataString = intent.getDataString();
Log.e(TAG,"dataString :" + dataString);
//todo耗时操作
}
这里跟Service的创建还是有区别的,IntentService自动处理了onStartCommand() 之类的回调方法,在IntentService中要避免实现Service中的回调方法。
在AndroidMainfest.xml中注册Service
<service
android:name=".service.MyService"
android:exported="false"/>
开启一个IntentService
public void clickMy(View view){
Intent mServiceIntent = new Intent(this,MyService.class);
mServiceIntent.setData(Uri.parse(dataUrl));
startService(mServiceIntent);
}
通过创建一个Intent,实现发送IntentService,同时可以携带各类参数。
IntentService同UI交互
IntentService在处理完耗时操作以后,如何跟Fragment或者Activity进行交互呢? 通过发送本地广播,IntentService可以将结果发送出去,在想要处理结果的Fragment或者Activity中注册本地广播,就可以实现IntentService和UI的交互。下面代码贴出完整的IntentService类,以及Activity中监听IntentService返回结果的处理。
IntentService
public class MyService extends IntentService{
private static final String TAG = "MyService";
private BroadcastNotifier mBroadcaster = new
BroadcastNotifier(this);
public MyService() {
super("MyService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
String dataString = intent.getDataString();
Log.e(TAG,"dataString :" + dataString);
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);
//模拟耗时操作,更多时候是网络请求
try {
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_DOING);
Thread.sleep(5 * Integer.valueOf(dataString));
mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_FINSHIED);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这里通过BroadcastNotifier管理不同状态广播的发送
public class BroadcastNotifier {
private LocalBroadcastManager mBroadcaster;
public BroadcastNotifier(Context context){
mBroadcaster = LocalBroadcastManager.getInstance(context);
}
public void broadcastIntentWithState(int status){
// The Intent contains the custom broadcast action for this app
Intent localIntent = new Intent();
localIntent.setAction(Constants.BROADCAST_ACTION);
//将statue放入intent中
localIntent.putExtra(Constants.EXTENDED_DATA_STATUS,status);
localIntent.addCategory(Intent.CATEGORY_DEFAULT);
//发送广播
mBroadcaster.sendBroadcast(localIntent);
}
}
通过LocalBroadcastManager发送一个本地广播,本地广播也就是只有当前应用能够监听的广播事件。
接下来看Activity中处理广播事件
public class MainActivity extends AppCompatActivity {
private String dataUrl = "1000";
private static final String TAG = "MainActivity";
private DownloadStateReceiver mDownloadStateReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter statusIntentFilter = new IntentFilter(
Constants.BROADCAST_ACTION);
statusIntentFilter.addCategory(Intent.CATEGORY_DEFAULT);
mDownloadStateReceiver = new DownloadStateReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(mDownloadStateReceiver,statusIntentFilter);
}
public void clickMy(View view){
Intent mServiceIntent = new Intent(this,MyService.class);
mServiceIntent.setData(Uri.parse(dataUrl));
startService(mServiceIntent);
}
@Override
protected void onDestroy() {
if(mDownloadStateReceiver != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(mDownloadStateReceiver);
super.onDestroy();
}
private class DownloadStateReceiver extends BroadcastReceiver{
public DownloadStateReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getIntExtra(Constants.EXTENDED_DATA_STATUS,Constants.STATE_ACTION_STARTED)){
case Constants.STATE_ACTION_STARTED:
Log.e(TAG,"开始执行耗时任务");
break;
case Constants.STATE_ACTION_DOING:
Log.e(TAG,"正在执行耗时任务");
break;
case Constants.STATE_ACTION_FINSHIED:
Log.e(TAG,"完成耗时任务");
break;
}
}
}
}
很简单,通过注册对应Action的广播,就可以监听IntentService中不同状态下发出的广播信息,在监听到状态后,进行了简单的日志打印.
08-30 14:35:50.148 26467-26467/? E/MainActivity: 开始执行耗时任务
08-30 14:35:50.148 26467-26467/? E/MainActivity: 正在执行耗时任务
08-30 14:35:55.149 26467-26467/? E/MainActivity: 完成耗时任务
其中Constants用来存储一些广播的Action名称以及一些不同的Statue
public class Constants {
public static final String BROADCAST_ACTION = "com.intentservice.service.BROADCAST";
public static final String EXTENDED_DATA_STATUS = "com.intentservice.service.STATUS";
public static final int STATE_ACTION_STARTED = 0;
public static final int STATE_ACTION_DOING = 1;
public static final int STATE_ACTION_FINSHIED = 2;
}
至此,IntentService相信已经掌握的差不多了。
记得之前参加过一个面试,面试官有问道是否了解IntentService,之前从来没有接触过这个类,因此无奈的回答了不了解。然后面试官又问我,那你们更新apk之类的后台服务是如何实现的,我的回答是通过在Service中开启子线程来实现异步方法处理,然后通过handler实现结果回调。
有毛病吗?没毛病。有毛病吗?当然有毛病啊。既然Google提供了IntentService,为何我还要多此一举呢。IntentService不仅实现了Service在子线程中执行,也完全跟UI脱离了关系,你可以再任何页面通过监听广播实现IntentService结果的监听。而且,你真的能保证自己实现的Service异步操作能比得过Google吗?
因此,强烈建议大家理解并真正使用IntentService。