上一篇详细介绍了Android——HandlerThread浅析
1. 简介
在Android应用的程序中,如果Service中处理耗时的操作,很容易出现ANR的现象,通常的做法就是,在onStartCommon方法中开启一个子线程然后内部执行耗时的操作,在执行完毕后如果停止服务,还需要手动的在子线程的run方法中调用stopSelf()来停止服务。
这里有一个问题,频繁的创建关闭子线程,对系统资源开销是非常大的,于是引入了IntentService,其实IntentService,就是Service 和 HandlerThread 的结合体
区别service:
- IntentService是继承Service的,那么它包含了Service的全部特性,也包含service的生命周期。
- 与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行耗时操作
- IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务
特点:
- IntentService是Service类的子类,用来处理异步请求
- IntentService是借助于消息队列实现的,所以任务的执行顺序就是一个queue单链表的形式
- 由于是单线程(一个工作线程),所以所有的任务需要排队执行
(多次启动IntentService,每个耗时操作将以队列的方式在IntentService的onHandleIntent回调方法中依次执行,执行完自动结束。)
优缺点:
- 1、使用方便,代码简洁,不再需要我们自己像Service里面还要去手动创建线程;
- 2、当操作完成时,我们不用手动停止Service
- 1、由于是单个的worker thread,所以任务需要排队,不适合大多数的多任务情况
- 2、不适合用于bindService()这样的启动方式的
工作流程图:
实现步骤:
- 1、定义IntentService的子类:传入线程名称、复写onHandleIntent()方法
- 2、在Manifest.xml中注册服务
- 3、在Activity中开启Service服务
2. 举个例子
步骤1:定义IntentService的子类:传入线程名称、复写onHandleIntent()方法
package com.example.xxx;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class myIntentService extends IntentService {
public MyIntentService() {
//构造函数,继承父类方法
//参数=工作线程的名字
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
//重写onHandleIntent()方法,实现耗时任务的操作
//根据Intent的不同进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate(); //单独开了个线程处理
}
/*复写onStartCommand()方法*/
//默认实现将请求的Intent添加到工作队列里
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步骤2:在AndroidManifest.xml中注册服务
<service android:name=".myIntentService">
<intent-filter>
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步骤3:在Activity中开启Service服务
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//同一服务只会开启一个工作线程
//在onHandleIntent函数里依次处理intent请求。
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次启动
}
结果:
3. 源码分析
1、IntentService是Service的子类,拥有Service的所有生命周期方法,内部还有一个ServiceHandler
// IntentService.java源码
public abstract class IntentService extends Service {
// ServiceHandler继承Handler,创建时必须传入一个Looper
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
protected abstract void onHandleIntent(@Nullable Intent intent);
}
2、onCreate()的源码,创建HandlerThread。
// IntentService.java源码
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
IntentService服务开启后就会创建Handler线程(HandlerThread),并获取当前线程的Looper对象,然后关联到Handler中。
3、接下来是onStart()源码,调用mServiceHandler
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
当你启动IntentService的时候,就会产生一条附带startId和Intent的 Message并发送到MessageQueue中,接下来Looper发现MessageQueue中有Message的时候,就会停止Handler 处理消息。
4、handleMessage处理消息的代码
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
接着调用 onHandleIntent((Intent)msg.obj),这是一个抽象的方法,其实就是我们要重写实现的方法,我们可以在这个方法里面处理我们的工作.当任务完成时就会调用stopSelf(msg.arg1)这个方法来结束指定的工作。
5、stopSelf(msg.arg1)
回调完成后回调用 stopSelf(msg.arg1),注意这个msg.arg1是个int值,相当于一个请求的唯一标识。每发送一个请求,会生成一个唯一的标识,然后将请求放入队列,当全部执行完成(最后一个请求也就相当于getLastStartId == startId),或者当前发送的标识是最近发出的那一个(getLastStartId == startId),则会销毁我们的Service.如果传入的是-1则直接销毁。
6、onDestroy()
@Override
public void onDestroy() {
mServiceLooper.quit();
}
服务结束后调用这个方法 mServiceLooper.quit()使looper停下来。
4. 总结
IntentService本质是采用Handler & HandlerThread方式:
- 通过HandlerThread单独开启一个名为IntentService的线程
- 创建一个名叫ServiceHandler的内部Handler
- 把内部Handler与HandlerThread所对应的子线程进行绑定
- 通过onStartCommand()传递给服务intent,依次插入到工作队列中,并逐个发送给onHandleIntent()
- 通过onHandleIntent()来依次处理所有Intent请求对象所对应的任务
通过复写方法onHandleIntent(),再在里面根据Intent的不同进行不同的线程操作就可以了
注意事项:工作任务队列是顺序执行的。
如果一个任务正在IntentService中执行,此时你再发送一个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕才开始执行。
原因:
- 由于onCreate() 方法只会调用一次,所以只会创建一个工作线程;
- 当多次调用 startService(Intent) 时(onStartCommand也会调用多次)其实并不会创建新的工作线程,只是把消息加入消息队列中等待执行,所以,多次启动 IntentService 会按顺序执行事件;
- 如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。
5. IntentService与Service的区别
- 从属性 & 作用上来说 Service:依赖于应用程序的主线程(不是独立的进程 or线程)
不建议在Service中编写耗时的逻辑和操作,否则会引起ANR; - IntentService:创建一个工作线程来处理多线程任务
- Service需要主动调用stopSelft()来结束服务,而IntentService不需要(在所有intent被处理完后,系统会自动关闭服务)
6. IntentService与其他线程的区别
- IntentService内部采用了HandlerThread实现,作用类似于后台线程;
- 与后台线程相比,IntentService是一种后台服务,优势是:优先级高(不容易被系统杀死),从而保证任务的执行。
对于后台线程,若进程中没有活动的四大组件,则该线程的优先级非常低,容易被系统杀死,无法保证任务的执行