目录
[一、什么是 Android 服务?](#一、什么是 Android 服务?)
[1. 官方定义](#1. 官方定义)
[2. 服务 vs 线程:关键区别](#2. 服务 vs 线程:关键区别)
[二、Service 的生命周期](#二、Service 的生命周期)
[1. 两种启动模式的生命周期](#1. 两种启动模式的生命周期)
[2. 相关方法详解](#2. 相关方法详解)
[3. StartService启动Service](#3. StartService启动Service)
[3.1 启动服务(Started Service)的生命周期:](#3.1 启动服务(Started Service)的生命周期:)
[3.2 onStartCommand详解:](#3.2 onStartCommand详解:)
[3.3 深入理解 onStartCommand() 的 flags 参数](#3.3 深入理解 onStartCommand() 的 flags 参数)
**START_FLAG_REDELIVERY(重新投递标志)**
[flags 的总结要点](#flags 的总结要点)
[3.4 关于 startId 的核心机制](#3.4 关于 startId 的核心机制)
[关键区别:stopSelf() vs stopSelf(startId)](#关键区别:stopSelf() vs stopSelf(startId))
[3.5 onStartCommand()的返回值详解](#3.5 onStartCommand()的返回值详解)
[4. BindService启动Service](#4. BindService启动Service)
[4.1 启动服务(Started Service)的生命周期:](#4.1 启动服务(Started Service)的生命周期:)
[4.2 在 Activity 中绑定服务](#4.2 在 Activity 中绑定服务)
[ServiceConnection 详细解析](#ServiceConnection 详细解析)
[int flags - 常用标志组合](#int flags - 常用标志组合)
[4.3 创建 Service 类](#4.3 创建 Service 类)
[5. 两种启动方式混合开发(Android 8之前版本)](#5. 两种启动方式混合开发(Android 8之前版本))
[5.1 生命周期:](#5.1 生命周期:)
一、什么是 Android 服务?
1. 官方定义
服务(Service)是Android四大组件之一,是一个在后台执行长时间运行操作 的应用组件,不提供用户界面。服务可由其他应用组件启动,即使用户切换到其他应用,服务仍可在后台运行。
核心特征:
无用户界面:不像Activity,服务没有可视化界面
后台运行:独立于UI,适合长时间操作
组件级别:是Android组件,有独立生命周期
优先级较高:比普通后台线程更不容易被系统杀死
2. 服务 vs 线程:关键区别
| 对比维度 | Service(服务) | Thread(线程) |
|---|---|---|
| 本质 | Android组件 | Java执行单元 |
| 生命周期 | 由系统管理,复杂 | 由程序员控制 |
| 运行线程 | 默认在主线程 | 在独立线程 |
| 优先级 | 较高,不易被杀死 | 较低,容易被回收 |
| 通信方式 | Intent、Binder、Broadcast等 | Handler、回调等 |
| 用途场景 | 音乐播放、下载、位置跟踪 | 计算密集型任务 |
默认是在主线程运行 的,所以最好不要直接在服务中执行耗时操作。
java
// ❌ 错误示范:在服务中直接执行耗时操作
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 这会阻塞主线程,导致ANR!
downloadLargeFile(); // 耗时操作
return START_STICKY;
}
// ✅ 正确做法:在服务中启动工作线程
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(() -> {
downloadLargeFile(); // 在子线程执行
}).start();
return START_STICKY;
}
二、Service 的生命周期
1. 两种启动模式的生命周期

从上图的生命周期,我们可以知道,Android中使用Service的方式有两种:
- StartService()启动Service
- BindService()启动Service
还有一种,就是启动Service后,绑定Service!两者结合的混合开发模式。后面会讲解各自的优势。
2. 相关方法详解
- onCreate() :当Service第一次被创建后立即回调该方法,该方法在整个生命周期 中只会调用一次!如果服务已运行,再次启动不会调用此方法
- onDestory() :当Service被关闭时会回调该方法,该方法只会回调一次!
- onStartCommand(intent,flag,startId) :早期版本是onStart(intent,startId), 当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法, 但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调 onStartCommand()方法!
- IBinder onOnbind(intent) :该方法是Service都必须实现的方法,该方法会返回一个 IBinder对象,app通过该对象与Service组件进行通信!
- onUnbind(intent):当该Service上绑定的所有客户端都断开时会回调该方法!
- onRebind(Intent intent) :当之前已解绑的客户端重新绑定到服务时回调该方法(前提是 onUnbind() 返回了 true)
| 特性 | BindService | StartService |
|---|---|---|
| 生命周期 | 绑定后创建,所有客户端解绑后销毁 | 调用 startService() 创建,stopService() 或 stopSelf() 销毁 |
| 通信方式 | 双向通信(通过 IBinder) | 单向通信(通过 Intent) |
| 使用场景 | 长时间交互、RPC调用 | 后台执行任务、无需交互 |
接下来结合生命周期详细讲解上面的方法。
3. StartService启动Service
3.1 启动服务(Started Service)的生命周期:
java
Context.startService() → onCreate() → onStartCommand()
→ 运行中 → stopSelf()/stopService() → onDestroy()
①首次启动会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service 进入运行状态,如果再次调用StartService启动Service,将不会再创建新的Service对象, 系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法!
② 但这样的Service与它的调用者无必然的联系,就是说当调用者结束了自己的生命周期, 但是只要不调用stopService,那么Service还是会继续运行的!
③无论启动了多少次Service,只需调用一次StopService即可停掉Service
3.2 onStartCommand详解:
多次调用StartService方法,会反复调用onStartCommand。所以可以通过传入不同的intent来让服务执行不同的业务逻辑
java
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: 启动服务,startId=" + startId);
// 获取传递的数据
String action = intent.getAction();
String url = intent.getStringExtra("download_url");
// 处理具体业务逻辑
if ("DOWNLOAD".equals(action)) {
startDownload(url, startId);
}else if(....){
.......其他的逻辑.......
}
// 返回值决定服务被杀死后的行为
return START_REDELIVER_INTENT;
}
参数解析:
Intent intent:启动服务时传递的Intent
int flags:启动标志位
START_FLAG_REDELIVERY:之前已传递过此Intent
START_FLAG_RETRY:之前启动失败重试
int startId:每次启动的唯一ID,用于stopSelf(startId)
3.3 深入理解 onStartCommand() 的 flags 参数
flags 参数在 onStartCommand() 中是一个提示信息,告诉服务本次启动的特殊情况。
**START_FLAG_REDELIVERY(重新投递标志)**
java
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 检查是否是重新投递
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
Log.d(TAG, "这是之前未完成的任务重新投递");
// 这里可以重新处理未完成的任务
recoverUnfinishedTask();
} else {
Log.d(TAG, "这是新的启动请求");
// 正常处理新任务
processNewTask();
}
// 注意:只有 START_REDELIVER_INTENT 返回值才会触发重新投递
return START_REDELIVER_INTENT;
}
触发场景:
-
服务在 onStartCommand() 返回 START_REDELIVER_INTENT 后
-
服务被系统杀死前,任务未完成(未调用 stopSelf(startId))
-
系统重新创建服务并重新传递同一个 Intent
java
典型流程:
1. Activity 启动服务传递 Intent A
2. 服务 onStartCommand() 返回 START_REDELIVER_INTENT
3. 服务开始处理任务(但未完成)
4. 系统内存不足杀死服务
5. 稍后系统重新创建服务
6. onStartCommand() 被调用,flags 包含 START_FLAG_REDELIVERY
7. 服务知道这是未完成的任务,可以恢复处理
实际应用:文件下载服务
java
public class DownloadService extends Service {
private Map<Integer, DownloadTask> runningTasks = new HashMap<>();
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String url = intent.getStringExtra("url");
int fileId = intent.getIntExtra("file_id", -1);
// 检查是否是重新投递
boolean isRedelivery = (flags & Service.START_FLAG_REDELIVERY) != 0;
if (isRedelivery) {
Log.i(TAG, "重新投递下载任务: fileId=" + fileId);
// 从数据库或缓存中恢复下载进度
long downloadedBytes = loadDownloadProgress(fileId);
resumeDownload(url, fileId, downloadedBytes, startId);
} else {
Log.i(TAG, "新的下载任务: fileId=" + fileId);
// 开始新的下载
startNewDownload(url, fileId, startId);
}
// 重要:必须返回 START_REDELIVER_INTENT 才能触发重新投递
return START_REDELIVER_INTENT;
}
private void downloadComplete(int startId) {
// 任务完成,调用 stopSelf(startId)
// 这样即使服务被杀死,也不会重新投递这个任务
stopSelf(startId);
runningTasks.remove(startId);
}
}
**START_FLAG_RETRY(重试标志)**
java
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 检查是否是重试
if ((flags & Service.START_FLAG_RETRY) != 0) {
Log.w(TAG, "上次启动失败,系统正在重试");
// 这里可以记录重试次数,防止无限重试
int retryCount = intent.getIntExtra("retry_count", 0);
if (retryCount > 3) {
Log.e(TAG, "重试次数超过3次,放弃任务");
stopSelf(startId);
return START_NOT_STICKY;
}
// 增加重试次数
intent.putExtra("retry_count", retryCount + 1);
}
// 尝试执行任务
try {
performTask(intent, startId);
return START_STICKY;
} catch (Exception e) {
Log.e(TAG, "任务执行失败,等待系统重试", e);
// 返回 START_STICKY,系统会重试
return START_STICKY;
}
}
触发场景:
-
服务 onStartCommand() 返回后立即崩溃
-
服务启动过程中发生异常
-
系统会尝试重新启动服务
java
典型流程:
1. Activity 启动服务
2. 服务 onStartCommand() 被调用
3. 服务立即崩溃(未返回或抛出异常)
4. 系统检测到崩溃
5. 系统尝试重新启动服务
6. onStartCommand() 再次被调用,flags 包含 START_FLAG_RETRY
flags 的总结要点
| 标志位 | 触发条件 | 典型应用 | 返回值要求 |
|---|---|---|---|
| START_FLAG_REDELIVERY | 1. 服务返回 START_REDELIVER_INTENT 2. 任务未完成服务被杀死 3. 系统重新创建服务 | 1. 恢复未完成任务 2. 防止重复处理 3. 状态恢复 | START_REDELIVER_INTENT |
| START_FLAG_RETRY | 1. 服务启动立即崩溃 2. onStartCommand() 抛出异常 | 1. 崩溃恢复 2. 重试机制 3. 错误处理 | 通常是 START_STICKY |
关键理解:
-
flags 是只读信息,告诉你本次启动的特殊情况
-
需要配合返回值使用,特别是 START_FLAG_REDELIVERY
-
合理使用 stopSelf(startId) 可以避免不必要的重新投递
-
在实际开发中,大多数情况下不需要特别处理 flags,除非你实现了需要恢复状态的复杂任务
一句话总结 :flags 参数是系统给服务的"小纸条",告诉服务"这次启动有点特别,上次可能没完成或者失败了",让服务可以做出相应的处理策略。
3.4 关于 startId 的核心机制
startId 是每次调用 startService() 时系统生成的唯一递增ID,✅ 每次调用 startService() ,无论来自哪个上下文,都会获得唯一且递增的 startId。用于:
-
标识不同的启动请求
-
配合 stopSelf(startId) 精确停止服务
-
跟踪任务执行状态
当多个任务执行的时候,需要知道哪个任务是否完成。startId就起了很关键的作用。
java
public class TaskService extends Service {
private Map<Integer, TaskInfo> currentTasks = new ConcurrentHashMap<>();
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 创建任务信息
TaskInfo task = new TaskInfo(startId, intent, System.currentTimeMillis());
currentTasks.put(startId, task);
// 在子线程执行任务
executor.execute(() -> {
try {
// 执行任务
processTask(intent);
// 任务完成,精确停止
task.completed = true;
stopSelf(startId);
} catch (Exception e) {
Log.e(TAG, "任务失败: " + startId, e);
task.failed = true;
} finally {
currentTasks.remove(startId);
}
});
return START_REDELIVER_INTENT;
}
// 精确停止服务
public void stopSelf(int startId) {
currentTasks.remove(startId);
// 检查是否还有其他任务在运行
if (currentTasks.isEmpty()) {
// 没有任务了,真正停止服务
super.stopSelf();
} else {
Log.i(TAG, "还有 " + currentTasks.size() + " 个任务在运行,不停止服务");
}
}
}
当服务处理多个并发请求时,需要区分不同的处理上下文。startId 作为每次 startService() 调用的唯一标识,配合 stopSelf(startId) 可以确保只有在某个特定请求处理完毕后,才停止对应的服务实例,而不会影响其他正在处理中的请求。
关键区别:stopSelf() vs stopSelf(startId)
| 方法 | 行为 | 使用场景 |
|---|---|---|
stopSelf() |
立即停止服务 | 只有一个任务,或确定没有其他请求 |
stopSelf(startId) |
只停止匹配这个 startId 的请求 | 服务可能同时处理多个请求 |
3.5 onStartCommand()的返回值详解
java
// 1. START_STICKY(粘性)
// 服务被杀死后,系统会重新创建服务,但不会重新传递最后一个Intent
// 简单理解就是会执行onCreate() 但不会执行onStartCommand()
// 适合音乐播放、下载等需要自动恢复的服务
return START_STICKY;
// 2. START_NOT_STICKY(非粘性)
// 服务被杀死后,除非有新的Intent传递,否则不会重新创建
// 适合一次性任务,如处理即时消息
return START_NOT_STICKY;
// 3. START_REDELIVER_INTENT(重新传递)
// 服务被杀死后,系统会重新创建服务并重新传递最后一个Intent
// 适合必须完成的任务,如下载文件
return START_REDELIVER_INTENT;
// 4. START_STICKY_COMPATIBILITY(兼容性粘性)
// START_STICKY的兼容版本,不保证onStartCommand会被调用
return Service.START_STICKY_COMPATIBILITY;
对比表格:
| 返回值 | 服务被杀死后 | 重新传递Intent | 适用场景 |
|---|---|---|---|
| START_STICKY | 重新创建 | ❌ 不传递 | 音乐播放、后台运行 |
| START_NOT_STICKY | 不自动重建 | ❌ 不传递 | 即时消息处理 |
| START_REDELIVER_INTENT | 重新创建 | ✅ 重新传递 | 文件下载、必须完成的任务 |
| START_STICKY_COMPATIBILITY | 可能重建 | ❌ 不传递 | 兼容旧版本 |
4. BindService启动Service
BindService(绑定服务)是一种客户端-服务器 接口,允许组件(如 Activity)与服务进行双向通信。服务在绑定时创建,所有客户端解绑时销毁。
4.1 启动服务(Started Service)的生命周期:
java
Context.bindService() → onCreate() → onBind() → 客户端通过IBinder交互
→ 所有客户端unbind() → onUnbind() → onDestroy()
4.2 在 Activity 中绑定服务
java
public class MainActivity extends AppCompatActivity {
private MyBindService myService;
private boolean isBound = false;
// ServiceConnection 用于监听绑定状态
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 绑定成功
MyBindService.LocalBinder binder = (MyBindService.LocalBinder) service;
myService = binder.getService();
isBound = true;
// 调用服务方法
int data = myService.getData();
myService.performAction("start");
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 意外断开连接(如服务崩溃)
isBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 绑定服务
Intent intent = new Intent(this, MyBindService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解绑服务
if (isBound) {
unbindService(connection);
isBound = false;
}
}
}
java
public abstract boolean bindService(
Intent service, // 要绑定的服务意图
ServiceConnection conn, // 绑定状态监听器
int flags // 绑定标志
);
ServiceConnection 详细解析
java
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 成功绑定时的回调
// name 就是绑定的服务的 ComponentName,包含包名和类名
Log.d(TAG, "服务连接成功: " + name);
// 获取服务实例(根据 Binder 类型不同)
if (service instanceof MyService.LocalBinder) {
MyService.LocalBinder binder = (MyService.LocalBinder) service;
myService = binder.getService();
isBound = true;
// 初始化工作
initializeAfterBinding();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 非正常断开连接(服务崩溃或被杀死)
Log.w(TAG, "服务异常断开: " + name);
isBound = false;
myService = null;
// 重新连接逻辑
scheduleReconnection();
}
@Override
public void onBindingDied(ComponentName name) {
// Android 8.0+,服务所在进程死亡
Log.e(TAG, "绑定死亡: " + name);
// 需要清理资源并重新绑定
cleanupAndRebind();
}
@Override
public void onNullBinding(ComponentName name) {
// 服务返回 null(服务端 onBind() 返回 null)
Log.e(TAG, "服务返回 null 绑定: " + name);
// 可能是服务配置错误
}
};
如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开 连接时调用onServiceDisconnected (CompanentName)方法。注意: 主动通过unBindService() 方法断开并不会调用上述方法!
int flags - 常用标志组合
java
// 组合1:标准绑定 + 自动创建
int flags = Context.BIND_AUTO_CREATE;
// 组合2:重要服务(提高优先级)
flags = Context.BIND_AUTO_CREATE |
Context.BIND_IMPORTANT;
// 组合3:前台服务绑定
flags = Context.BIND_AUTO_CREATE |
Context.BIND_FOREGROUND_SERVICE;
// 组合4:调试模式
flags = Context.BIND_AUTO_CREATE |
Context.BIND_DEBUG_UNBIND |
Context.BIND_ALLOW_OOM_MANAGEMENT;
| 标志组合 | 优先级 | 内存不足时 | 特点 |
|---|---|---|---|
| BIND_AUTO_CREATE | 默认 | 可能被杀 | 标准模式 |
| + BIND_IMPORTANT | 较高 | 较难被杀 | 提升进程优先级 |
| + BIND_FOREGROUND_SERVICE | 前台级 | 很难被杀 | 前台服务优先级 |
| + BIND_ALLOW_OOM_MANAGEMENT | 较低 | 更容易被杀 | 允许系统回收 |
| 标志 | 值 | 说明 |
|---|---|---|
| BIND_AUTO_CREATE | 1 | 服务不存在时自动创建 |
| BIND_DEBUG_UNBIND | 2 | 调试解绑问题,会减慢解绑速度 |
| BIND_NOT_FOREGROUND | 4 | 不将服务提升到前台优先级 |
| BIND_ABOVE_CLIENT | 8 | 服务优先级高于客户端 |
| BIND_ALLOW_OOM_MANAGEMENT | 16 | 允许系统在内存不足时杀死服务 |
| BIND_WAIVE_PRIORITY | 32 | 放弃优先级提升 |
| BIND_IMPORTANT | 64 | 重要绑定(提高优先级) |
| BIND_ADJUST_WITH_ACTIVITY | 128 | 根据Activity状态调整优先级 |
| BIND_FOREGROUND_SERVICE | 4096 | 绑定到前台服务 |
| BIND_FOREGROUND_SERVICE_WHILE_AWAKE | 8192 | 设备唤醒时绑定到前台服务 |
| BIND_INCLUDE_CAPABILITIES | 4096 | 包含功能信息 |
4.3 创建 Service 类
java
public class MyBindService extends Service {
private final IBinder binder = new LocalBinder();
// 自定义 Binder 类
public class LocalBinder extends Binder {
// 可以定义各种提供外界交互的方法
// 下面是提供方法的两种方式
MyBindService getService() {
// 返回自身,以供外界调用public方法
return MyBindService.this;
}
// 直接提供一个方法
public int getCount()
{
return 11;
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
// 所有客户端解绑时调用
// 返回父类的值 默认返回false(根据父类的实际情况而定)
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
// onRebind 通常用于:
// 1. 恢复连接状态
// 2. 重新注册监听器
// 3. 恢复数据同步
// 调用父类实现
super.onRebind(intent);
}
// 服务提供的公共方法
public int getData() {
return 42;
}
public void performAction(String action) {
// 执行具体操作
}
}
如果我们再绑定后直接关掉Activity的话会报错, 然后会自动调用onUnbind和onDestory方法!
onRebind() 是 Android Service 生命周期中的一个特殊回调方法。它并不是每次绑定服务时都会调用,而是在特定的"重新绑定"场景下触发。
onRebind() 的调用需要满足 四个严格条件:
曾经有客户端绑定过服务:服务至少被绑定过一次
所有客户端都已解绑:当前没有任何客户端绑定到该服务
上次解绑时返回了 true:在 onUnbind() 方法中明确返回了 true
有新的客户端重新绑定:在服务未被销毁的情况下,又有新的客户端请求绑定
如果这四个条件同时满足,系统就会调用 onRebind(),而不是通常的 onCreate() → onBind() 流程。
java
首次绑定:
onCreate() → onBind()
客户端解绑:
onUnbind() → (如果返回true,服务保持运行,不销毁)
重新绑定:
onRebind() ← 直接调用,不经过 onCreate()
最终销毁:
(当服务真正需要销毁时) → onDestroy()
5. 两种启动方式混合开发(Android 8之前版本)
混合模式服务是一种既可以启动(start) 又可以绑定(bind) 的服务。在这种模式下,服务的生命周期会变得更复杂,需要同时满足两种模式的要求才会被销毁。
5.1 生命周期:
混合模式(同时启动和绑定):
startService() → onCreate() → onStartCommand()
↓
bindService() → onBind()
↓
运行中(既有启动又有绑定)
↓
unbindService() → onUnbind() // 服务继续运行
↓
stopService()/stopSelf() → onDestroy()
启动服务 :通过
startService()启动,即使Activity销毁,服务仍可继续运行(Android 8之前)绑定服务 :通过
bindService()绑定,可以与Activity进行通信生命周期 :服务只有在既没有绑定也没有启动时才会被销毁
场景1:先启动后绑定的完整流程
java
1. startService() → onCreate() → onStartCommand()
2. bindService() → onBind()
3. unbindService() → onUnbind() // 服务继续运行
4. stopService()/stopSelf() → onDestroy()
场景2:先绑定后启动的流程
java
1. bindService() → onCreate() → onBind()
2. startService() → onStartCommand()
3. unbindService() → onUnbind() // 服务继续运行
4. stopService()/stopSelf() → onDestroy()
对于 Android 8+(API Level 26+),混合模式服务的行为有很大变化 ,特别是对于后台服务的限制。这个在后面章节会详细讲解。