Android 中的 Service 类拓展和 IntentService 介绍

IntentService 作为 Service 的子类,它能够将我们需要的操作放到 工作线程 进行处理。通过重写 onHandleIntent() 方法,此方法会自动处理我们发出的每一个 Intent,在工作线程中执行任务。

前言

众所周知,但我们想要启动一个 Service,需要在另一个组件中调用 startService(),从而调用 Service 中的 onStartCommand() 方法.

Service 拥有自己的生命周期,并可以在后台无限期的运行(和 Windows 中的 Service 很像)。

注意1:即使是调用者(启动服务的组件)被销毁,都不能影响 Service 的运行。我们需要在另一个组件中调用 stopService() 或者在服务中调用 stopSelf() 来停止服务。
注意2:我见到到一些 Android 开发人员,因为对 Service 不熟悉,想当然的认为每当我们启动了一个 Service,也就意味了开了一个子线程,这种想法是错误的。
实际情况是:默认情况下,服务和服务声明的应用运行在同一个进程中,而且运行在主线程中。所以说,如果 Service 需要处理一些耗时操作,我们需要在 Service 中开启一个工作线程来处理这些任务。

这里有两种 Service 供我们使用:

  • Service : 这是服务的基类。默认情况下运行于主线程中。
  • IntentService : Service 的子类,能够将我们的请求在工作线程中逐一处理。如果你的业务需求是不需要同时处理多个请求,那么 IntentService 将会是你最好的选择。我们只需实现 onHandleIntent() 方法即可,该方法会处理每个请求,并在后台执行。

IntentService

我们来详细说一说 IntentService 的功能:

  • 创建默认的 工作线程,来处理在 onStartCommand() 中接受到的所有 Intent;
  • 创建 工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这种机制,让我们告别多线程的苦恼;
  • 在处理完工作队列中的所有 Intent 后,会自动停止服务,也就是说,你永远不必调用停止 Service 的方法;
  • 提供 onBind() 的默认实现(返回 null);
  • 提供 onStartCommand() 的默认实现,可将 Intent 依次发到 工作队列 中和 onHandleIntent() 实现;

也就是说,我们只需提供 IntentService 的构造函数,以及在 onHandleIntent() 来处理相应逻辑即可。

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

/**
* 这个构造器是必须要实现的,你需要在里面调用父类的构造方法,
* 并为工作线程提供一个名字。
*/

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

/**
* 当我们启动了此 IntentService,此服务会在默认的工作线程中调
* 用此方法,当方法结束后,service 也会适时的被销毁。
*/

@Override
protected void onHandleIntent(Intent intent) {
// 我们在这里处理一些耗时操作,比如下载一个文件 etc.
// TODO
}
}
注意:通常情况下,我们只需实现一个构造器和 onHandleIntent() 方法即可,如果你还想重写其他回调方法(如:onCreate(), onStartCommand() etc.),你需要确保你调用了父类的实现,以便 IntentService 能够正确的处理工作线程的生命周期。

如下所示:

1
2
3
4
5
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}

除了 onHandleIntent() 方法外,我们还有一个无需调用父类实现的方法是 onBind()(仅当绑定服务时,才需要实现).

Service 拓展

通过上面一节的介绍,我们可以知道利用 IntentService 可以非常方便的同步处理一些 Intent,但是当业务场景转换到需要同时执行多个 Intent 时,IntentService 就不能够满足我们的需求了,这时,我们需要扩展 Service 类。

下面的示例代码中,我们对每一个请求,都放在工作线程中执行,且每次只处理一个请求。

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class TestService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;

// 此 Handler 用来接受从线程中发出的消息
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 我们在这里处理一些耗时操作,比如下载一个文件 etc.
// 作为示例,我们这里仅仅模拟一下休眠 5 秒的操作
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 通过 startId 来停止服务,避免错误的停止了其他服务
stopSelf(msg.arg1);
}
}

@Override
public void onCreate() {
// 初始化一个子线程,来处理耗时操作,避免阻塞主线程
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();

// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

// 对于每一个请求,我们通过发送 Message 来启动一个操作,同时,为每一个 Message 设置 startId,以便在操作完成时,正确的停止服务
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);

// 返回 START_STICKY,当服务被杀死时,系统会重启此服务
return START_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
// 未绑定服务,直接返回 null 即可
return null;
}

@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}

这个示例并不是同时处理多个 Intent,因为不太建议这么做,如果实在需要,我们可以在 onStartCommand() 方法中对每个 Intent 单独创建一个子线程,然后立即运行这些线程。

注意:onStartCommand() 方法必须返回一个整数类型。用于描述系统应该如何处理如果服务被终止了的情况。我们返回的整数类型必须是下面三种值其一:

如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但绝对不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

本文参考:

文章有帮助到您?不妨打赏博主一碗拉面或一杯咖啡的小费吧 :-D!