前言

Service相关知识和注意点的记录及整理。

目录

目录结构

1. 基础知识

  • 定义:服务,是Android四大组件之一, 属于 计算型组件
  • 作用:提供 需在后台长期运行的服务

    如:复杂计算、音乐播放、下载等

  • 特点:无用户界面、在后台运行、生命周期长

2. 生命周期

官方说明图

注意

  • 若一个Service被startService()多次启动,onCreate()也只会调用一次,只有onStartCommand()可以多次调用,其他只能调用一次。
  • bindService()启动的服务当所有客户端都解绑后,系统会销毁服务。
  • 异常情况下结束Service的生命周期不走OnDestory方法,比如系统清理,或者ANR结束

PS:
服务在被系统杀死后该如何继续运行

onStartCommand()返回一个整数
  • START_NOT_STICKY: 不会重建服务。除非还存在未发送的Intent,当服务不再是必需,并且应用程序能够简单地重启那些未完成的工作时,这是避免服务运行的最安全的选项。
  • START_STICKY: 重建服务 & 调用onStartCommand(),但不会再次送入上一个Intent,而是用null intent来调用onStartCommand(),除非还有启动服务的intent未发送完,那么这些剩下的Intent会继续发送(适用于媒体播放器类似服务,他们不执行命令,但需要一直运行并随时待命)
  • START_REDELIVER_INTENT: 重建服务 & 用上一个已送过的Intent调用onStartCommand()。任何未发送完的intent也都会依次送入。

3. 启动方式

讲到上述生命周期,便涉及到了两种启动方式:startService 和 bindService

3.1 第一种方式:通过startService启动Service

通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。

3.2 第二种方式:通过bindService启动Service

  • bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
  • client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
  • bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁

4. Service基本使用

服务端继承Service,重写onCreate().onStartCommand().onBind().onDestroy().四个方法。清单文件manifest注册Service

4.1 StartService方式

Service端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PushService extends Service{

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

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

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

Activity端:

1
2
3
4
5
6
7
//启动Service
Intent intent = new Intent(this, PushService.class);
startService(intent);

//停止Service
Intent intent = new Intent(this, PushService.class);
stopService(intent);

4.2 bindService方式

想要Service支持bindService调用:

  • 在Service的onBind()方法中返回IBinder类型的实例。
  • onBInd()方法返回的IBinder的实例需要能够返回Service实例本身

    最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得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
30
31
32
33
34
35
36
37
38
public class PushService extends Service{

private static final String TAG = "PushService";

//client 可以通过Binder获取Service实例
public class MyBinder extends Binder {
public PushService getService() {
return PushService.this;
}
}

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
//通过binder实现调用者client与Service之间的通信
return new MyBinder();
}

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

//getServiceName是Service暴露出去供client调用的公共方法
public String getServiceName() {
return TAG;
}
}

Client端:
调用方式与start不同

  • 创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
  • 当执行到onServiceConnected回调时,可通过IBinder实例得到Service实例对象,这样可实现client与Service的连接。
  • onServiceDisconnected回调被执行时,表示client与Service断开连接,在此可以写一些断开连接后需要做的处理。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //创建ServiceConnection类型实例
    private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder binder) {

    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
    };

    //启动绑定
    Intent intent = new Intent(this, TestTwoService.class);
    intent.putExtra("name", "ABCD");
    bindService(intent, conn, BIND_AUTO_CREATE);

    //解绑
    unbindService(conn);

PS:
保证Service不被杀死?

  1. onStartCommand方式中,返回START_STICKY
  2. 提高Service的优先级
  3. 提升Service进程的优先级
  4. 在onDestroy方法里重启Service
  5. 系统广播监听Service状态
  6. 将APK安装到/system/app,变身为系统级应用

5. Activity和Service通信方式

使用方式自行查阅

5.1 bind启动开放公共方法。

5.2 回调接口的方式

5.3 通过广播(推荐)

5.4 观察者模式

6. Service和IntentService区别

6.1 区别

IntentService包含了Service所有特性。
不同:IntentService可以处理耗时操作。Service是依赖于主线程的,所以不能直接处理耗时操作。而IntentService会将任务依次插入到任务队列中,并逐个发送给onHandleIntent()来处理。省去了手动开线程的麻烦,并且自动停止Service

6.2 IntentService使用场景

很明显在需要按顺序,并且在后台处理请求时,优先IntentService。

6.3 源码分析

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
@Override
public void onCreate() {
super.onCreate();
// HandlerThread继承自Thread,内部封装了 Looper
// 通过实例化andlerThread新建线程并启动
// 所以使用IntentService时不需要额外新建线程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();

// 获得工作线程的 Looper,并维护自己的工作队列
mServiceLooper = thread.getLooper();

//将上述获得Looper与新建的mServiceHandler进行绑定
//新建的Handler是属于工作线程的。 ->> 分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}

/**
* 分析1 ServiceHandler绑定的是后台线程的Looper,而在Service停止时,Looper退出了,内存很快会被回收
**/
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}

// IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行,->> 分析2
onHandleIntent((Intent)msg.obj);
// 执行完调用 stopSelf() 结束服务。
stopSelf(msg.arg1);
}
}

/**
* 分析2
* onHandleIntent()是一个抽象方法,使用时需要重写的方法
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);

6.4 IntentService总结

  • 通过HandlerThread 单独开启1个工作线程:IntentService
  • 创建1个内部 Handler :ServiceHandler
  • 绑定 ServiceHandler 与 IntentService
  • 通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作- 队列中 & 逐个发送给 onHandleIntent()
  • 通过onHandleIntent() 依次处理所有Intent对象所对应的任务

PS:

  1. 不适用bindService启动IntentService
  2. 在所有Intent被处理完后,系统自动关闭服务
  3. 复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作
  4. IntentService优先级比普通线程高

结束