SummerXia's blog


  • Home

  • Archives

Service探究系列之启动方式

Posted on 2019-11-10

在上一篇中我们介绍了Android Service的基础概念和如何创建及启动Service。在这一篇中我们来详细探索一下Service的两种启动方式:startService和bindService。

一、startService

当你的服务不需要与页面组件进行交互或只有提示性交互时,一般会使用startService启动服务,让其独立于组件而运行,如在后台定位用户位置信息。

通过 startService() 方法启动服务,其生命周期如下图所示:

start_lifecycle

下面分别介绍其生命周期方法:

onCreate() :服务第一次被创建时调用,主要用来处理一些服务所必须的初始化操作。注意,这里不要做任何耗时或繁重的操作。

onStartCommand() :通过startService启动服务,在服务创建后会即可调用该方法,也是服务处理任务的主要方法,其一般实现如下:

1
2
3
4
5
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
workThread.doSomething(intent);
return START_NOT_STICKY;
}

可以看到该方法需要返回一个 int 类型的值,该值用于描述服务被系统终止后,希望系统后续如何处理该服务。只能取以下三个常量中的一个:

  1. START_NO_STICKY:如果系统在 onStartCommand() 返回值后终止了服务,除非有待传递的挂起Intent,否则系统不会重现创建并启动服务。这是最安全的选项,可以避免服务及未完成任务状态的错乱,在合适的时机再次启动服务来处理待处理的任务。此模式适用于一些需要获取结果数据,但允许在内存紧张时停止服务且后续不需要再次恢复的任务,如从服务端请求数据。
  2. START_STICKY:如果系统在 onStartCommand() 返回值后终止了服务,则系统会重新创建并调用 onStartCommand() 方法,但不会重新传递最后一个Intent,因此该模式下 onStartCommond() 方法中的参数 intent 有可能为null,必须进行判空处理。此模式适用于不执行命令、但无限期运行的任务,如媒体播放器。
  3. START_REDELIVER_INTENT:如果系统在 onStartCommand() 返回值后终止了服务,则系统会重新创建服务,并将最后一个 Intent 传递给 onStartCommand() 。所有挂起的 Intent 均会依次传递。此模式适用于主动执行,且应立即恢复的任务,如文件下载或上传。

onStartCommand() 接收三个参数:第一个 intent 就是通过startService传递进来的Intent,可以携带一些数据;第二个 flags 为标识此次请求服务的额外数据,该标识由系统处理并传递,通常请求下为0,当服务被系统终止后再次启动服务时,系统会根据 onStartCommand() 方法的返回值传递该标识, 取值为 START_FLAG_REDELIVERY 或 START_FLAG_RETRY,一般我们不会用到这个参数;第三个 startId 是一个标识本次请求的唯一Integer值,主要用于停止服务标识(stopSelfResult(int)),如果调用stopSelfResult的 startId 和最后一个调用服务分配的startId不同,则不会停止服务。

停止服务有两种方式:一是在组件中调用 stopService() 停止服务;二是在 Service 内部调用 stopSelf() 停止服务;

注意:再次提醒下,Service 的所有生命周期方法默认都是运行在启动服务的进程中的主线程里的,如果有耗时或阻塞性操作,需要在 onStartCommand 方法中另起工作线程处理任务,也可以使用 IntentService 代替

onDestroy:服务销毁时调用,一般在这里需要将service中的资源做释放处理。

通过 startService() 启动的服务可以通过 Toast 或 状态栏通知 来通知用户任务处理的状态,也可以通过广播的方式将执行结果传递给接受者。

二、bindService

如果你需要在服务启动后,与服务进行交互或IPC,应该以bindService()来启动服务,并实现Service的onBind()方法,返回一个IBinder实例来进行与服务的交互或IPC。以下为一个简单的onBind()实现示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Hello service onBind, intent:" + (intent != null ? intent.toString() : "null"));
return new HelloBinder();
}

public void sayHello(String name) {
Toast.makeText(getApplicationContext(), "Hello " + name + " from HelloService", Toast.LENGTH_SHORT).show();
}

public class HelloBinder extends Binder {

public HelloService getService() {
return HelloService.this;
}
}

如上所示onBind()方法需要返回一个IBinder类型的参数,IBinder是Android中进行IPC重要的类,很多地方都有其身影。这里我们只是简单地继承了IBinder的子类Binder,并提供了一个获取当前服务实例的方法。关于IBinder在不同服用中推荐的不同实现方式我们放到后面再详细展开说明,这里先关注以绑定启动服务的方法或步骤。

实现了onBind()方法后,我们就可以在组件中以绑定的方式来启动服务了。以绑定的方式启动服务比直接启动服务要稍微复杂些,除了需要封装Intent来显式声明服务外,还必须提供ServiceConnection实例,后者会监控与服务的连接,并能从中获取onBind()返回的IBinder实例,以便组件与服务进行交互或IPC。以下为bindService()启动服务的示例:

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

// 创建ServiceConnection实例
private ServiceConnection helloServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected");
mBound = true;
HelloService.HelloBinder binder = (HelloService.HelloBinder) service;

helloService = binder.getService();
helloService.sayHello("MainActivity");
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected");
mBound = false;
helloService = null;
}
};

// 在Activity中启动服务
private void bindService() {
Intent intent = new Intent(MainActivity.this, HelloService.class);
intent.putExtra("count", count);
bindService(intent, helloServiceConnection, Context.BIND_AUTO_CREATE);
}

@Override
private void onClick(View v){
if(mBound && helloService != null){
helloService.sayHello("CLICK");
}
}

以上即为绑定启动服务的示例,下面我们来一一分析下ServiceConnection的实现和bindService()的参数及绑定启动后Service的生命周期。

ServiceConnection是一个接口,当创建客户端和服务的连接时,Android系统会调用其 onServiceConnected(ComponentName name, IBinder service)方法,并将服务在onBind()方法返回的IBinder作为其方法的第二个参数传递进来,客户端可以使用该参数与绑定的服务进行通信交互。当客户端和服务断开连接时,Android系统会调用其onServiceDisconnected(ComponentName name)方法,通知客户端与服务的连接断开了。

bindService()的第三个参数为绑定服务的选项标识,可能的取值为以下几种:

  1. Context. BIND_AUTO_CREATE:在绑定服务时自动创建服务,是绑定服务时常用的标识,其他的几个不常用;
  2. Context. BIND_NOT_FOREGROUND:不允许服务的进程提升到前台;
  3. Context. BIND_ABOVE_CLIENT:服务的优先级高于客户端,当OOM时会优先销毁application,然后才销毁服务;
  4. Context. BIND_ALLOW_OOM_MANAGEMENT:允许进程持有的服务超出自身的内存管理边界,使服务得以运行;
  5. Context. BIND_WAIVE_PRIORITY:不会超出目标服务进程的计划或内存管理优先级,允许服务像在后台运行的普通应用进程那样被后台LRU列表管理;

以单纯的bindService()启动服务,组件可以通过unbindService()来解除与服务的绑定关系,同一个服务允许多个客户端同时绑定服务,当所有与服务绑定的组件都解绑后,服务也会随之销毁。其生命周期如下所示:

start_lifecycle

同一个服务也允许以startService()和bindService()两种方式同时启动,这种情况下服务的声明周期跟两种启动方式都相关,只有所有与服务绑定的组件都解绑了且调用了停止服务,此时服务才会销毁。可以用下图来表示Service可能的生命周期:

start_lifecycle

总结下绑定启动服务的步骤:

  1. 创建ServiceConnection实例,用来监控服务的连接情况。
  2. 创建声明服务的显示Intent,调用bindService()方法来绑定启动服务。
  3. 在ServiceConnection实例的onServiceConnected()回调方法中接收onBind()返回的IBinder实例,并调用该实例的公共方法获取Service实例或Service其他可供客户端调用的对象实例。
  4. 客户端就可以通过获取到的服务相关实例来与Service进行交互了。

三、IBinder的几种实现方式

绑定服务时onBind()需要返回一个IBinder实例,其实现方式有以下三种方式:继承Binder类、使用Messenger创建IBinder和AIDL。这三种方式有不同的适用场景和实现难度,接下来一一介绍。

1. 继承Binder

如果你的服务只供自身应用使用,且无需进行跨进程工作,则优先推荐使用该方式,使用此方法让客户端访问Service的公共方法或资源。这也是实现IBinder最简单的方式,上面的示例中我们就是使用这种方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
* 提供给客户端调用的公共方法
* @param name
*/
public void sayHello(String name) {
Toast.makeText(getApplicationContext(), "Hello " + name + " from HelloService", Toast.LENGTH_SHORT).show();
}

/**
* 自定义的Binder对象
*/
public class HelloBinder extends Binder {

public HelloService getService() {
return HelloService.this;
}
}

继承Binder的一般使用方式可以总结如下:

  1. 在你的服务中,创建可执行以下某种操作的 Binder 实现类:
    • 包含客户端可调用的公共方法。
    • 返回当前的 Service 实例,该实例中包含客户端可调用的公共方法。
    • 返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法。
  2. 从 onBind() 回调方法返回此 Binder 实例。
  3. 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。

使用Messenger

如果你的服务需要进行跨进程通讯(IPC),且无需执行多线程处理,推荐使用Messenger为你提供IBinder实例。Messenger比使用AIDL更简单,其会将请求加入到一个队列中,然后让其按照队列顺序一次执行一个请求,不存在线程不安全的问题(多个进程中的不同线程调用同一个服务)。以下是使用Messenger的示例和大致流程:

创建Service类,内部实现处理消息的Handler和以该Handler实例为参数的Messenger实例,在onBinder()方法中返回Messenger实例创建的IBinder实例。

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

/**
* 远程Service,通过Messenger进行IPC
*/
public class RemoteService extends Service {
private final String TAG = "RemoteService";
public static final int REMOTE_HELLO = 1;
public static final int REGISTER_CLIENT = 2;
/**
* 处理客户端Messenger发送过来消息的Handler
*/
private Handler mHandler;
/**
* Service端的Messenger
*/
private Messenger mServiceMessenger;

@Override
public void onCreate() {
super.onCreate();
mHandler = new RemoteHandler(getApplicationContext());
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
mServiceMessenger = new Messenger(mHandler);
IBinder binder = mServiceMessenger.getBinder();
return binder;
}

@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "remote service onUnbind");
return super.onUnbind(intent);
}

@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "remote service onDestroy");
}

static class RemoteHandler extends Handler{
private Context context;
RemoteHandler(Context applicationContext){
context = applicationContext;
}

@Override
public void handleMessage(Message msg) {
switch (msg.what){
case REMOTE_HELLO:
Toast.makeText(context, "Hello from remote service", Toast.LENGTH_SHORT).show();
break;
default:

break;
}
}
}
}

配置RemoteService单独的进程,清单文件里配置如下:

1
2
3
4
5
6

<service
android:name=".service.RemoteService"
android:description="@string/app_name"
android:exported="true"
android:process="com.summer.remote_service" />

在Activity中绑定服务,并通过ServiceConnection获取服务传递过来的IBinder实例,并以此实例构建Messenger,这样客户端就可以通过该Messenger与服务进行通讯了,如下:

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

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i(TAG, "onServiceConnected, componentName:" + componentName.toString() + ", iBinder:" + iBinder.toString());
// 以iBinder构建Messenger
messenger = new Messenger(iBinder);
sayHelloByRemoteService();
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(TAG, "onServiceDisconnected, componentName:" + componentName.toString());
}
};

/**
* 通过Messenger与RemoteService通讯
*/
private void sayHelloByRemoteService() {
Message msg = Message.obtain(null, RemoteService.REMOTE_HELLO);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

至此组件就可以通过Messenger同服务进行通讯了,但上面的示例中只实现了由客户端主动同服务端通讯,而无法由服务端主动向客户端发起通讯。可以通过客户端在Message设置replyTo来实现。以下为完整的服务端和客户端双向通讯的示例代码:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

/**
* 远程Service,通过Messenger进行IPC
*/
public class RemoteService extends Service {
private final String TAG = "RemoteService";
public static final int REMOTE_HELLO = 1;
public static final int REGISTER_CLIENT = 2;
/**
* 处理客户端Messenger发送过来消息的Handler
*/
private Handler mHandler;
/**
* Service端的Messenger
*/
private Messenger mServiceMessenger;
/**
* 接收的客户端的Messenger
*/
private static Messenger clientMessenger;

@Override
public void onCreate() {
super.onCreate();
mHandler = new RemoteHandler(getApplicationContext());
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
mServiceMessenger = new Messenger(mHandler);
IBinder binder = mServiceMessenger.getBinder();
return binder;
}

@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "remote service onUnbind");
return super.onUnbind(intent);
}

@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "remote service onDestroy");
}

static class RemoteHandler extends Handler{
private Context context;
RemoteHandler(Context applicationContext){
context = applicationContext;
}

@Override
public void handleMessage(Message msg) {
switch (msg.what){
case REMOTE_HELLO:
Toast.makeText(context, "Hello from remote service", Toast.LENGTH_SHORT).show();
// 通过接收到的客户端Messenger发消息给客户端
Message message = Message.obtain(null, 0, 110, 0);
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case REGISTER_CLIENT:
// 接收客户端传递过来的Messenger
clientMessenger = msg.replyTo;
break;
default:

break;
}
}
}
}

Activity中的代码

// 处理服务端发送过来的消息的Handler
private Handler clientHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int obj = msg.arg1;
Toast.makeText(getApplicationContext(), obj + "", Toast.LENGTH_SHORT).show();
}
};

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i(TAG, "onServiceConnected, componentName:" + componentName.toString() + ", iBinder:" + iBinder.toString());
// 构建与服务端通讯的Messenger
messenger = new Messenger(iBinder);
// 构建客户端的Messenger,通过Message传递过服务端
clientMessenger = new Messenger(clientHandler);

Message obtain = Message.obtain(null, RemoteService.REGISTER_CLIENT);
// 将Messenger传递给服务端
obtain.replyTo = clientMessenger;
try {
messenger.send(obtain);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.i(TAG, "onServiceDisconnected, componentName:" + componentName.toString());
}
};

总结下使用 Messenger 创建 IBinder 实例的大致流程如下:

  1. 在服务内创建处理消息的 Handler ,并以此作为参数构建 Messenger实例;
  2. 在 onBind()方法中返回由 Messager 创建的 IBinder 实例;
  3. 在绑定服务的 ServiceConnection 的 onConnected()方法中接收服务的 IBinder 实例,并以此为参数构建 Messenger 实例,然后客户端就可以通过此 Messenger 发送消息给服务端,并在服务端的 Handler里处理此消息。
  4. 如果想要服务端主动与客户端交互,需要在客户端中创建自己的 Handler 并以此为参数构建 Messenger,将此 Messenger 作为使用服务端 IBinder 构建的 Messenger发送 Message 的 replyTo 参数发送给服务端,服务端使用 replyTo 的 Messenger 与客户端进行主动交互。

AIDL

如果你的服务需要进行跨进程通讯,且需要多线程操作,可以使用 AIDL 来创建 IBinder 实例,你需要自己来保证多线程操作时的线程安全。AIDL实现起来比较复杂,后续作为单独的章节来介绍,这里就不展开了。

Service探究系列之基础

Posted on 2019-11-10

一、Service简介

Service 是Android四大组件之一,是一种可以执行长时间操作而不提供界面的应用组件。Service 启动后,当用户切换到其他应用时,其依然可在后台继续运行。此外,组件也可以绑定到 Service 并与之进行交互,甚至是执行进程间通信(IPC)。主要是用来处理一些用户无感知的耗时后台操作,如:网络数据处理、播放音乐、定位信息、执行文件IO操作、数据库读写操作等。

二、创建Service

创建一个 Service 非常简单,只需继承 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
public class HelloService extends Service {
private final String TAG = "HelloService";

@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "HelloService onCreate");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Hello service onStartCommand, intent:" + (intent != null ? intent.toString() : "null") + ", flags:" + flags + ", startId:" + startId);
int count = intent.getIntExtra("count", 0);
Log.i(TAG, "the count is " + count);
return super.onStartCommand(intent, flags, startId);
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Hello service onDestroy");
}
}

上面的 HelloService 继承自 Service,并实现了其生命周期方法。其中 onCreate() 和 onDestroy() 同 Activity 的生命周期方法一样,在service创建或销毁时会被系统调用的;onStartCommand() 和 onBind() 方法也属于 Service 的生命周期方法,但是其根据启动方式不同而被回调的方法不同,本例中只关注 onStartCommand() 方法(后续我们在探索service的两种启动方式时再详细说明 onBind() 的使用),当service是以非绑定的方式启动时,在回调完onCreate()方法后,会立即回调onStartCommand()方法,并将附带参数以Intent的方式传递进来,service的重要工作也是在这个方法中实现的。

Service 同 Activity 一样也必须在AndroidManifest中声明,之后才能使用,以下为HelloService的清单声明示例:

1
2
3
4
<service
android:name=".service.HelloService"
android:description="@string/service_des"
android:exported="false" />

其中name是必须配置的,其他的根据自己的需要选择性配置,以下是service可配置的所有属性,各个属性的功能及取值说明请参考Service清单配置说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:foregroundServiceType=["connectedDevice" | "dataSync" |
"location" | "mediaPlayback" | "mediaProjection" |
"phoneCall"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>

创建完HelloService并在清单文件中声明后,我们就可以启动service了。你可以在Activity和ContentProvider中通过Intent来启动服务,如下所示:

1
2
3
Intent intent = new Intent(context, HelloService.class);
intent.putExtra("count", count);
startService(intent);

运行结果如下:

1
2
3
I/HelloService: HelloService onCreate
I/HelloService: Hello service onStartCommand, intent:Intent { cmp=com.summer.servicedemo/.service.HelloService (has extras) }, flags:0, startId:1
I/HelloService: the count is 1

注意:为了确保应用的安全性,请始终使用显示Intent启动服务,且不要为Service声明intent_filter。从Android5.0(API 21)开始,通过隐式Intent调用bindService会抛出系统异常

至此我们的service就启动起来了。service启动后,其生命周期即独立于启动的组件了,即使系统已经销毁启动服务的组件,该服务依然可在后台无限期地运行。因此,当任务完成后应该主动调用并停止服务,可由其他组件在外部调用 stopService() 或在服务内部调用 stopSelf() 方法来停止服务,详细的使用方式我们会在接下来的文章中分析。

默认情况下,服务与服务声明所在的应用运行于同一进程,并且运行在该应用的主线程中。如果在服务内部要执行密集型或阻塞性操作,会降低该应用Activity的性能,同时还有可能引起ANR(服务内任务一般在20s执行不完就会ANR)。因此,如果是密集型或阻塞性操作,请在服务内启动新线程来执行对应任务(IntentService默认封装了工作线程,可直接继承IntentService,将任务放到其工作线程中执行)。

总结下使用service的大致流程:

  1. 继承Service或其子类(IntentService),并实现其生命周期方法,编写任务处理代码;
  2. 在清单文件中声明Service;
  3. 在其他组件中启动Service;

三、继承IntentService类

通常情况下服务无需同时处理多个请求(实际上,这种多线程操作服务也是危险的),Android提供了一种单一线程并顺序执行任务的Service子类IntentService,其具有以下特点:

  1. 在其内部创建了默认的工作线程,用来处理外部传递给 onStartCommand() 的所有Intent。
  2. 内部维护了工作队列,用于将Intent逐一传递给 onHandleIntent() 方法,避免多线程同步问题。
  3. 在处理完所有的任务后,自动停止服务,不需要你关心何时停止的问题。
  4. 默认实现了 onStartCommand() 方法,并将其接收到的Intent依次发送到工作队列和 onHandleIntent() 处理。
  5. 默认实现了 onBind() 方法(返回null),也就是外部通过 bindService() 方法启动服务是无效的,只能通过 startService() 启动服务。

以下是继承IntentService的示例:

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
public class HelloIntentService extends IntentService {

public HelloIntentService() {
this("HelloIntentService");
}

/**
* @param 工作线程的名称,一般用来测试使用;
*/
public HelloIntentService(String name) {
super(name);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
Thread thread = Thread.currentThread();
// 这里获取的线程名称即为在构造方法中设置的name
Log.i(getClass().getSimpleName(), "thread name:" + thread.getName());
// do something
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

@Override
public void onDestroy() {
super.onDestroy();
Log.i(getClass().getSimpleName(), "service onDestroy");
}
}

如上例所示,我们只需要继承IntentService并实现 onHandleIntent() 方法即可,将你要处理的任务放到 onHandleIntent() 方法中,IntentService就会逐个处理传递进来的任务。我们在外部用一个线程池来模拟多线程启动服务的情况,并查看下HelloIntentService是如何处理任务的,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private void testIntentService() {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
service.execute(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this, HelloIntentService.class);
intent.putExtra("index", index);
startService(intent);
}
});
}
}

运行结果如下:

1
2
3
4
5
6
11-08 09:16:37.150  7749  7829 I HelloIntentService: thread name:IntentService[HelloIntentService]
11-08 09:16:38.152 7749 7829 I HelloIntentService: thread name:IntentService[HelloIntentService]
11-08 09:16:39.154 7749 7829 I HelloIntentService: thread name:IntentService[HelloIntentService]
11-08 09:16:40.156 7749 7829 I HelloIntentService: thread name:IntentService[HelloIntentService]
11-08 09:16:41.158 7749 7829 I HelloIntentService: thread name:IntentService[HelloIntentService]
11-08 09:16:42.158 7749 7749 I HelloIntentService: service onDestroy

可以看出任务大约每隔1s执行一次,所有任务都执行完后service也就停止了。

注意:如果你还要重写其他回调方法,如:onCreate()、onStartCommand()或onDestroy(),必须确保调用super实现,以保证 IntentService 能正确处理工作线程的生命周期。

四、总结

Service 作为Android四大组件之一,具有生命周期独立于启动组件而在后台不断运行的特性,主要用来处理一些用户无感知的耗时操作。通过创建服务、清单文件中声明服务、在其他组件中启动服务三个步骤来完成服务的使用,并可由外部组件手动停止服务,也可以在自身内部将服务停止。

如果你的任务需要另开线程处理,且不需要多线程处理,应首选继承 IntentService 实现你的服务,其内置工作线程处理任务,并无需你关心其生命周期。

Hello World

Posted on 2018-06-27

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

SummerXia

SummerXia's blog

3 posts
1 tags
© 2019 SummerXia
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4