CodeXiaoMai

CodeXiaoMai的博客


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

IntentService源码解析

发表于 2019-09-24 更新于 2019-11-08 分类于 Android 阅读次数:
本文字数: 6k

IntentService 是 Service 的子类,当然也是用于处理耗时操作的,通过 startService(Intent) 启动,并且会在工作线程中处理每个 Intent,并在操作结束或不正常时自行停止。

要看懂 IntentService 的源码,必须熟悉 Handler 机制,不明白的建议先了解一下 Handler 机制。

IntentService 的用法

要使用 IntentService,只需要创建一个类继承 IntentService 并实现抽象方法 onHandleIntent(Intent)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class InitializeService extends IntentService {

private static final String ACTION_INIT = "service.action.INIT";

public InitializeService() {
super("InitializeService");
}

public static void start(Application context) {
Intent intent = new Intent(context, InitializeService.class);
intent.setAction(ACTION_INIT);
context.startService(intent);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
// 处理耗时操作
}
}

就这么简单,我们只需要把精力用在重写 onHandleIntent 方法处理耗时操作就可以了,也不用开启新的线程。

而如果直接使用 Service,恐怕就没那么简单了,我们需要处理很多事情,比如耗时操作要单独创建一个工作线程,操作结束后要停止 Service 运行等等。

那么 IntentService 是怎么实现将 Service 中复杂的逻辑简化为只需要实现一个 onHandleIntent 方法就可以的呢?

其实 IntentService 是通过对 Service 的各个生命周期进行处理,并结合了 Android 中强大的 Handler 机制来完成的。

下面就根据 Service 的生命周期,分析 IntentService 的实现原理。

先借用 Android 官网的 Service 生命周期图看一下:

service_lifecycle.png

下面以通过 startService 启动 IntentService 为例,进行分析。

OnCreate

下面是 IntentService 的 onCreate 方法的源码:

IntentService.java

1
2
3
4
5
6
7
8
9
10
11
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;

@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

创建一个 HandlerThread,也就是一个子线程,并启动这个线程。获取这个线程绑定的 Looper,再利用这个 Looper 创建一个 Handler,因为这个 Looper 是与 HandlerThread(一个新的工作线程)绑定的,所以与这个 Looper 关联的 Handler 也就在 HandlerThread 线程中接收与处理消息。

HandlerThread 启动后,必然会运行 run() 方法,接下来看一下 HandlerThread.run()。

HandlerThread.java

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
/**
* 该方法主要是标准的 Looper 的使用步骤
*/
@Override
public void run() {
mTid = Process.myTid();
// 1.
Looper.prepare();
synchronized (this) {
// 2. 将当前线程的 Looper 赋值给 mLooper,并通知正在等待使用 mLooper 的地方
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 3.
Looper.loop();
mTid = -1;
}

/**
* 此方法将返回与此线程关联的Looper。
* 如果未启动此线程或 isAlive() 返回false,此方法将返回null。
* 如果该线程已经启动,该方法将阻塞,直到looper被初始化。
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}

// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}

当 HandlerThread 启动后,在 run 方法中会调用 Looper.prepare() 方法创建 Looper 的实例并与 HandlerThread 线程相关联,然后调用 Looper.loop() 方法开始循环处理消息。

onStartCommand/onStart

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
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

/**
* 我们不应该在 InentService 中重写此方法,而应该重写 onHandleIntent(),
* 当 IntentService 收到一个请求时,系统会调用它。
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

/**
* onBind 方法默认返回 null,并且通过 bindService() 启动的 IntentService 的生命周期是:
* onCreate()->onBind()->onunbind()->onDestory();
* 这种方式不会调用 onStartCommand() 和 onStart() 方法,
* 所以不会将消息发送到消息队列中去,也就不会执行 onHandleIntent() 回调,
* 失去了 IntentService 的优势。所以不建议使用这种方式。
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}

onStartCommand 方法内部会调用 onStart 方法,onStart 方法中就是创建一个 Message 对象,并把 Intent 和 startId 保存到这个 Message 对象中,最后通过 mServiceHandler 发送消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
}
}

/**
* 这个方法在工作线程上执行,并处理耗时操作,一次只处理一个 Intent。
* 即若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,
* 这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行。
* 因为在 Service 没有被销毁前,onCreate()只会调用一次,所以只会创建一个工作线程;
* 当多次调用 startService(Intent)时(即 onStartCommand()也会调用多次),其实不会创建新的工作线程,只是把消息加入消息队列中并等待执行。
* 处理消息的 Looper,Handler,以及 onHandleIntent() 都是同一个线程,
* 所以当 Looper 从消息队列中取出一条消息后,只有在 onHandleIntent() 执行完毕后,才会取下一条消息。
* 当所有请求都被处理后,IntentService 就会停止,我们也不用手动停止。
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);

因为 ServiceHandler 是在子线程中接收消息,所以handleMessage() 方法自然也就在子线程中处理,onHandleIntent() 同样如此,最后处理结束后调用 stopSelf 方法停止 Service 运行。

为什么当有多个任务时 hanndleMessage 中 stopSelf(msg.arg1) 不会终止 Service?

因为在 onStartCommand 中会传入一个 startId,这个 startId 保存在 msg.arg1 中,因此每次调用 startService() 都会回调这个方法,所以每个 startId 也就不同,

也就是说,每当我们调用startService()启动一个服务时,不但会在其对应的 ServiceRecord 节点的 pendingStarts 里插入一个新的 StartItem 节点,而且会为这个 StartItem 节点生成一个新的 id 号,这个 id 号就是日后的 startId(生成id号时,采用的办法是最简单的加1操作)。

而在 stopSelf() 中传入的也是这个 startId,所以在关闭时,会关闭这个 startId 指定的任务,同时会判断是否是最后一个 startId,如果是最后一个才真正的结束 Service,否则不结束。

也就是按序从 ServiceRecord 的 deliveredStarts 列表中删除 StartItem 节点,直到所删除的是 startId 参数对应的 StartItem 节点,如果此时尚未抵达 ServiceRecord 内部记录的最后一个 start Id 时,则说明此次 stopSelf()操作没必要进一步结束service,那么直接 return false 就可以了。只有在所删除的 startItem 节点的确是最后一个 startItem 节点时,才会调用 bringDownServiceIfNeededLocked() 去结束 service。这就是为什么 IntentService 的 ServiceHandler 在处理完消息后,可以放心调用 stopSelf() 的原因。

参考文章https://my.oschina.net/youranhongcha/blog/785387

onDestroy

1
2
3
4
@Override
public void onDestroy() {
mServiceLooper.quit();
}

当 Service 真正停止时会回调 onDestroy() ,这时 mServiceLooper 会退出循环。

IntentService VS Service

运行线程 结束
IntentService 自动创建工作线程处理任务 自动结束,不需要处理。
Service 主线程
不能处理耗时操作,需编写代码创建子线程。
需要主动调用stopService()

IntentService VS 普通线程

线程优先级
IntentService 高
不容易被系统杀死
普通线程 低
若进程中无活动的四大组件,则该线程的优先级非常低,容易被系统杀死。
# Android # IntentService
Handler消息机制源码解析
Java内存模型
  • 文章目录
  • 站点概览
CodeXiaoMai

CodeXiaoMai

CodeXiaoMai的博客
12 日志
6 分类
19 标签
GitHub E-Mail
  1. 1. IntentService 的用法
  2. 2. OnCreate
  3. 3. onStartCommand/onStart
    1. 3.1. 为什么当有多个任务时 hanndleMessage 中 stopSelf(msg.arg1) 不会终止 Service?
  4. 4. onDestroy
  5. 5. IntentService VS Service
  6. 6. IntentService VS 普通线程
© 2019 CodeXiaoMai
|