IntentService为何物?

Author Avatar
第五季 2016.4.21
字数:1,637字 时长:7分钟
  • 微信扫一扫分享

  熟悉Android的人都知道,Androi系统为我们提供了一个专门处理后台耗时任务的组件,即Service。使用它可以很方便的完成一些需要长期执行但是却不需要UI界面的任务,比如音乐播放、同步数据库等等。那么我们今天的主角IntentService到底是何方神圣呢?

重要点梳理

  首先我们来看一下类的签名

1
public abstract class IntentService extends Service {}

  由此可知IntentService其实是一个Service,它继承自Service拥有Service的所有特性,当然青出于蓝而胜于蓝,IntentService比Service强大的地方在哪里呢?接着往下看:

ServiceHandler

  IntentService有一个内部类

1
2
3
4
5
6
7
8
9
10
11
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);
}
}

  可以看到ServiceHandler继承自Handler,实现了它的handleMessage方法,就是说IntentService内部实现了一套线程间发送消息和处理消息的机制。但是它只是处理了特定的事件即stopSelf。大家都知道stopSelf为Service处理完成释放资源的方法。不错,IntentService为我们启动了Service的“自毁”程序。

onCreate操作

  既然有了Handler消息的处理,那么是怎么初始化Handler的呢?请看:

1
2
3
4
5
6
7
8
9
10
11
12
13
@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();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

  在这个onCreate方法中初始化了一个HandlerThread,此处如果不理解HandlerThread是什么,可以简单理解为一个自带Handler功能的Thread,是的,它就是一个线程。这里通过实例化HandlerThread新建线程并启动,所以使用IntentService时不需要额外新建线程,并且拿到工作线程的Looper维护自己的工作队列,最后将Looper与新建的ServiceHandler绑定由其分发处理事件。
  由于onCreate()方法只会调用一次,所以只会创建一个工作线程;当多次调用 startService(Intent) 时(onStartCommand也会调用多次)其实并不会创建新的工作线程,只是把消息加入消息队列中等待执行,所以,多次启动 IntentService 会按顺序执行事件。

onCreate操作

  处理消息、以及Handler都准备好了,那么我们的消息到底是在什么时候添加的呢?

1
2
3
4
5
6
7
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

  从这里我们就能知道,每当启动一个IntentService时会把intent包装到message的obj中,然后发送消息,即添加到消息队列里。最后在消息处理的时候拿出这个intent作为事件处理的参数。参见ServiceHandler的handleMessage方法。

Redelivery

  除此之外IntentService还额外附赠了一个setIntentRedelivery方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT}, so if this process dies before
* {@link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}

  在源码中我们可以发现,该方法改变了boolean变量mRedelivery的值,而mRedelivery得值关系到onStartCommand的返回变量。

1
2
3
4
5
6
7
8
9
10
11
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

  可以看到,mRedelivery不同,会返回两个不同的标志START_REDELIVER_INTENT 和START_NOT_STICKY,那么他们有什么不同呢?
  区别就在于如果系统在服务完成之前关闭它,则两种类型就表现出不同了:
  START_NOT_STICKY型服务会直接被关闭,而START_REDELIVER_INTENT 型服务会在可用资源不再吃紧的时候尝试再次启动服务。由此我们可以发现,当我们的操作不是十分重要的时候,我们可以选择START_NOT_STICKY,这也是IntentService的默认选项,当我们认为操作十分重要时,则应该选择START_REDELIVER_INTENT 型服务。

与Service的异同

  1、Service任务默认执行于应用程序的主线程,而IntentService会创建一个工作线程来处理任务
  2、Service需要主动调用stopSelft()来结束服务,而IntentService不需要(在所有intent被处理完后,系统会自动关闭服务)
  3、此外:
    IntentService为Service的onBingd()方式提供了默认实现:返回null
    IntentService为Service的onStartCommand()方法提供了默认实现:将请求的intent添加到队列中

使用方法

由于IntentService为抽象类,因此我们在使用时必须继承它并实现它的onHandleIntent方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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) {
//根据Intent的不同进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
//do something
break;
case "task2":
//do something
break;
default:
break;
}
}


@Override
public void onCreate() {
super.onCreate();
}

//默认实现将请求的Intent添加到工作队列里
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
super.onDestroy();
}
}

可以看到很简单的继承只需要实现自己的onHandleIntent方法就可以了,然后使用它也很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//同一服务只会开启一个工作线程
//在onHandleIntent函数里依次处理intent请求。
Intent i = new Intent("balabala");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);

Intent i2 = new Intent("balabala");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);

startService(i); //多次启动,会将i再次加入到消息队列,处理,然后分发结果

  IntentService中,onBind()是默认返回null的,而采用bindService() 启动 IntentService的生命周期是:onCreate()—>onBind()—>onunbind()—>onDestory(),并不会调用onstart()或者onstartcommand()方法,所以不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作。切记!