人生的很多转折点是由不经意的人和事引发的,但是更多是由经意的人和事引发的。
相信我们一开始接触安卓的时候,书上都会介绍到IntentService,但是自从我们接触Handler多了,就遗忘掉了这个类了,最近在工作中一次偶然的机会使用到这个类,使用过程中发现这个类出乎意料的简单,简直是不学白不学,学习的过程中还能够对Handler的知识进一步深入了解一下。
介绍
IntentService是一个继承自Service的抽象子类,客户端通过startService发送请求传递Intent,开启一个服务并且用一个工作线程轮流解决每一个intent,当结束工作后自动关闭服务。因为这个类继承自Service,所以它依然拥有Service的特性,所以当我们在Service还没关闭的时候调用Service,那么不会创建新的Service,也就不会执行onCreate,直接触发的是startService的时候。
所有的请求都会在一个单一的工作线程处理,它们不会阻塞主线程的loop,但一次只会处理一个请求。如果你想下载10个文件,调用10次startService,那么就会依次下载10个文件。
构造方法
构造方法需要传递一个name参数,这个name字段主要用于命名工作线程,对调试比较重要。
public IntentService(String name) {
super();
mName = name;
}
不过大多数时候我们可以直接传递一个固定的字符串
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
// 创建一个子线程,这个子线程内部包含一个Handler
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
//使用上面线程的looper来创建一个Handler,
mServiceHandler = new ServiceHandler(mServiceLooper);
}
对于onBind,IntentService的默认实现是返回null,也可以理解为默认不与任何组件绑定,除非我们需要将它和某个组件绑定,不然不需要重写这个方法,但是出于对IntentService的设计考虑,我认为我们不应该重写这个方法,不然使用IntentService就没有意义了。
Handler消息机制
为了兼容新旧版本的安卓系统,对于onStart和onStartCommand都进行了兼容
@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);
//如果Service不幸被系统杀死了,START_REDELIVER_INTENT 表示重启之后重新重新发送这个Intent,START_NOT_STICKY表示不重启
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
可以看到IntentService实现了一个叫ServiceHandler的类
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
//发送消息到这个looper的messagequeue,也从这个messagequeue中获取消息
super(looper);
}
@Override
public void handleMessage(Message msg) {
//模板模式,onHandleIntent是抽象方法
onHandleIntent((Intent)msg.obj);
// 关闭指定startid的服务
stopSelf(msg.arg1);
}
}
构造方法传入了一个Looper,而handlerMessage中则是获取消息中的Intent,然后调用onHandleIntent方法,执行完之后就会根据startId来关闭service。
需要注意的是stopSelf(int startId )这个方法,这个方法会尝试关闭Service,为什么说是尝试?因为我们要知道Service,也就是服务,它可以向客户提供不止一次的服务。每次通过startService( )启动的服务,系统都会生成一个startId,也就是标记了一次服务,在onStartCommand中可以获得这个startId。所以只有当stopSelf里面的startId等于最后一次调用startService( )所生成的startId,才会真正停止服务,否则服务是不会停止的。
那么IntentService是怎么开启一个工作线程来执行任务呢?前面已经提及到,在onCreate里面,会开启一个线程,在线程内部looper轮询MessageQueue的消息,当有消息到达之后取出消息进行处理,执行handler的handleMessage方法,所以我们handler所实现的handle方法是在looper所在的线程处理的,这也就是为什么能够实现异步了。
如果我们有什么耗时操作,我们就可以直接放到IntentService里面执行。
应用
比如,很多时候我们会在Application中做一些第三方类库的初始化,但是当我们的类库越来越多,或者第三方类库的初始化越来越复杂的时候就会出现一个问题,开机时间过长,俗称(冷启动时间过长)。要知道,我们初始化我们的应用,做的可不仅仅是这些初始化第三方,如果都放在主线程中进行的话,那么势必会出现问题。而为了优化这个问题,我们可以尝试使用IntentService来进行一些第三方类库的初始化操作。好比如:
在查看代码的过程中,我发现了一个比较有趣的东西,就是activity中的runOnUiThread这个方法
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
代码很简单,如果不在主线程,那么就使用mHandler发送消息,如果在主线程,那么就直接执行。简而言之,实现异步的所有方法(异步的概念是不用阻塞当前的线程来等待处理结果,允许后续操作),仅且只有一种,那就是handler,而其余的所有方法,都是对handler的一种封装而已,有人可能会对IntentService的异步不是很了解,要知道,IntentService继承自Service,Service是一个在主线程运行的组件,只是这个组件运行在后台而已,看不到的东西俗称叫后台。所以在这个Service内,我们可以通过handleIntent来实现一些异步操作,当然了,我们可以在handleIntent中需要想办法把消息发送到主线程的handler,通知主线程操作已经完成了。
总结
当我们真正想了解一个类的时候,常常会发现研究代码的时候会涉及到越来越多的API,越来越多的机制和代码,即使简单如AsyncTask,虽然代码量不多,但是展开了研究也是够呛的。单纯多线程,锁机制等就已经很复杂了,这也是为什么任职要求里面总是有多线程开发这一点。
然而大多数人对多线程也只是局限于new一个Thread,重写run方法而已…