前言
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端:
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端:
//启动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端:
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断开连接,在此可以写一些断开连接后需要做的处理。
//创建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不被杀死?
- onStartCommand方式中,返回START_STICKY
- 提高Service的优先级
- 提升Service进程的优先级
- 在onDestroy方法里重启Service
- 系统广播监听Service状态
- 将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 源码分析
@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:
- 不适用bindService启动IntentService
- 在所有Intent被处理完后,系统自动关闭服务
- 复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作
- IntentService优先级比普通线程高