当我们执行文件上传,文件下载等耗时任务时,为了保证 App 退回到后台而降低被系统干掉的概率,使得该任务仍然能继续执行,我们通常会将这些任务放在后台 Service 中去处理,但是,Service 是运行在主线程中(不能执行耗时操作),因此必须在 Service 中创建子线程来执行。Android SDK 提供了 IntentService 来简化了这个过程。
IntentService 特点:
- IntentService 是继承自 Service 的抽象类,内部使用 HandlerThread + Handler 建立了异步线程来处理 后台 Service 耗时操作。
- 当耗时任务执行完后,IntentService 会自动停止,不需要手动去停止。
- 启动多次 IntentService ,每个耗时任务会在 onHandleIntent 中以队列形式依次执行。
IntentService 常规使用套路
- 继承自 IntentService 类,重写构造函数和 onHandleIntent() 方法。
- 在 onHandleIntent() 方法中处理耗时任务,运行于子线程中。
- 通过 startService() 方式启动 IntentService, 不能通过 bindService() 方式启动,因为 IntentService 中的 onBind() 方法返回 null。
以下代码,模拟每次点击按钮添加一个耗时任务到队列中,直到所有的任务执行完后,才自动停止 service。
/**
* UploadLogService.java
*/
public class UploadLogService extends IntentService {
public static final String ACTION_UPDATE_UI = "action_update_ui";
private static final String TAG = "UploadLogService";
public UploadLogService() {
super("upload-log-service");
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.d(TAG, "onStart: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return super.onBind(intent);
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
/**
* 运行于子线程中
*
* @param intent
*/
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null) {
String text = intent.getStringExtra("text");
Log.d(TAG, "onHandleIntent: " + text);
SystemClock.sleep(3000); // 模拟耗时任务
// 任务执行完成,广播通知更新 ui
Intent updateUiIntent = new Intent();
updateUiIntent.setAction(ACTION_UPDATE_UI);
updateUiIntent.putExtra("text", text);
LocalBroadcastManager.getInstance(this).sendBroadcast(updateUiIntent);
}
}
}
/**
* IntentServiceActivity.java
**/
public class IntentServiceActivity extends AppCompatActivity {
private int index;
private LinearLayout linearLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
linearLayout = findViewById(R.id.ll_layout);
// 注册广播
registerBroadcastReceiver();
}
// 注册本地广播
private void registerBroadcastReceiver() {
IntentFilter filter = new IntentFilter(UploadLogService.ACTION_UPDATE_UI);
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
}
// 更新 UI 的本地广播
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String text = intent.getStringExtra("text");
TextView textView = linearLayout.findViewWithTag(text);
textView.setText(text + ">>>>>>>> success");
}
};
public void click(View view) {
index++;
String text = "uploading log = " + index;
addTextView(text);
// 开启异步任务service
startService(text);
}
private void addTextView(String text) {
TextView textView = new TextView(this);
textView.setText(text);
textView.setTag(text);
linearLayout.addView(textView);
}
private void startService(String text) {
Intent intent = new Intent(this, UploadLogService.class);
intent.putExtra("text", text);
startService(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
}
由此得出结果:
(1)多次启动 IntentService ,onCreate() 只会执行一次,而 onStart() / onStartCommond() 会执行多次,异步任务会顺序执行,直到所有的任务执行完成才会执行 onDestroy() 停止 Service。
(2)onBind() / onUnbind() 没有执行,因为启动 IntentService 是通过 startService() 方式启动的,但是我们不能通过 bindService() 方式启动 IntentService,因为 IntentService 源码中的 onBind() 方法返回 null。
(3)停止服务后,后续的事件将不会执行,因为 IntentService 中的 onDestroy() 中调用了 looper 的 quit() 方法退出轮询。