目录
前言:为什么 Service 是 Android 开发的 “后台核心”?
1.3 Service 与 Thread、IntentService 的区别
二、Service 生命周期深度解析:两种启动方式的回调逻辑
2.2 启动型 Service(startService)的生命周期
2.3 绑定型 Service(bindService)的生命周期
三、onStartCommand () 返回值:决定 Service 被杀死后的行为
3.2 实战示例:START_REDELIVER_INTENT 实现断点续传
四、IntentService:无需手动管理线程的 Service
4.2 实战示例:用 IntentService 实现多任务下载
示例 3:Service 主动推送下载进度到 Activity
5.2 跨进程通信:AIDL(Android 接口定义语言)
示例 5:Service 通过广播通知 Activity 任务完成
方案 2:WorkManager(替代后台 Service,Android 8.0 + 推荐)
7.2 坑点 2:绑定 Service 后未解绑导致内存泄漏
7.3 坑点 3:Android 8.0 + 后台 Service 容易被杀死
7.4 坑点 4:IntentService 并发任务执行失败
7.5 坑点 5:跨进程通信时抛出 RemoteException

class 卑微码农:
def __init__(self):
self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
self.发量 = 100 # 初始发量
self.咖啡因耐受度 = '极限'
def 修Bug(self, bug):
try:
# 试图用玄学解决问题
if bug.严重程度 == '离谱':
print("这一定是环境问题!")
else:
print("让我看看是谁又没写注释...哦,是我自己。")
except Exception as e:
# 如果try块都救不了,那就...
print("重启一下试试?")
self.发量 -= 1 # 每解决一个bug,头发-1
# 实例化一个我
我 = 卑微码农()
前言:为什么 Service 是 Android 开发的 “后台核心”?
刚入门 Android 时,我曾一度混淆 Service 和 Thread—— 觉得 “后台干活” 就该用 Thread,直到遇到 “退出 Activity 后音乐还在播放”“后台下载文件需要持续运行” 的场景,才发现 Service 的不可替代性。

其实 Service 是 Android 四大组件中专门负责 “后台任务” 的角色,它没有 UI 界面,却能在后台持续运行(即使应用退到后台),还能与 Activity、其他组件通信。但很多开发者用不好 Service,要么遇到 “Service 被系统杀死”“后台任务卡顿” 的问题,要么因不懂绑定机制导致内存泄漏。
本文会把 Service 的基础概念、生命周期、启动方式、通信交互、保活方案、常见坑点全讲透。包含 9 个完整可运行的原创示例,从基础入门到进阶实战,新手能直接上手,进阶开发者能夯实基础、避开坑点。
建议先收藏,再跟着示例一步步实操,遇到问题可以在评论区交流~
一、Service 基础认知:到底什么是 Service?

1.1 一句话搞懂 Service 的核心作用
Service 是 Android 系统提供的 “后台任务容器”,核心作用是在后台执行长时间运行的操作,且不依赖 UI 界面。
简单说:Activity 负责 “前台交互”(比如点击按钮、显示页面),Service 负责 “后台干活”(比如音乐播放、文件下载、数据同步、定位追踪)。即使 Activity 销毁,Service 也能继续运行(前提是未被系统回收)。
1.2 必须澄清的 3 个常见误解
- 误解 1:Service 是独立线程?—— 错!Service 默认运行在主线程(UI 线程),不能直接做耗时操作(否则会导致 ANR);
- 误解 2:Service 会一直运行不被杀死?—— 错!当系统内存不足时,后台 Service 会被优先回收,需通过保活方案优化;
- 误解 3:用 Thread 就能替代 Service?—— 不能!Thread 没有生命周期管理,应用退到后台后容易被系统回收,且无法与其他组件灵活通信。
1.3 Service 与 Thread、IntentService 的区别
| 组件 | 运行线程 | 生命周期管理 | 核心优势 | 适用场景 |
|---|---|---|---|---|
| Service | 默认主线程 | 受系统组件管理 | 可后台持续运行、跨组件通信 | 音乐播放、数据同步、定位追踪 |
| Thread | 自定义子线程 | 无(需手动管理) | 轻量、灵活 | 临时耗时操作(如单次数据计算) |
| IntentService | 内置子线程 | 任务完成后自动销毁 | 无需手动管理线程 | 异步单次任务(如单次文件下载) |
1.4 Service 的两种核心类型
根据使用场景,Service 主要分为两种类型,后续所有用法都围绕这两种类型展开:
- 启动型 Service(startService):通过
startService()启动,一旦启动,Service 会独立运行,直到调用stopService()或自行停止,与启动者无直接通信; - 绑定型 Service(bindService):通过
bindService()绑定,启动者(如 Activity)与 Service 建立连接,可双向通信,启动者销毁时需解绑,否则会内存泄漏。
二、Service 生命周期深度解析:两种启动方式的回调逻辑

Service 的生命周期比 Activity 简单,但因启动方式不同,回调流程差异很大。这是掌握 Service 的核心,也是最容易踩坑的地方。
2.1 核心生命周期回调方法(6 个关键方法)
| 方法名 | 调用时机 | 核心作用 | 注意事项 |
|---|---|---|---|
| onCreate() | Service 首次创建时(仅调用 1 次) | 初始化资源(如创建线程、注册监听器) | 避免耗时操作(默认主线程) |
| onStartCommand() | 每次调用startService()时触发 | 接收启动意图,执行后台任务 | 需返回 int 类型结果(决定 Service 被杀死后的行为) |
| onBind() | 调用bindService()时触发(仅调用 1 次) | 返回 Binder 对象,建立组件与 Service 的通信通道 | 必须返回 Binder 实例(或 null,不允许通信) |
| onUnbind() | 所有绑定者解绑(调用unbindService())后触发 | 释放绑定相关资源 | 可返回 boolean(true 表示后续绑定会触发 onRebind) |
| onRebind() | onUnbind () 返回 true 时,再次绑定 Service 触发 | 重新建立通信连接 | 无需返回 Binder(复用之前的连接) |
| onDestroy() | Service 销毁时(仅调用 1 次) | 彻底释放资源(如停止线程、注销监听器) | 是 Service 生命周期的最后一个方法 |
2.2 启动型 Service(startService)的生命周期
核心流程:
启动 → onCreate () → onStartCommand () → 运行中 → stopService ()/stopSelf () → onDestroy () → 销毁
关键说明:
- 首次启动:执行
onCreate() → onStartCommand(); - 重复启动:不会调用
onCreate(),直接调用onStartCommand()(多次启动会多次触发); - 停止方式:① 外部调用
stopService(Intent);② Service 内部调用stopSelf();③ 系统内存不足时回收; - 示例验证:通过日志打印回调顺序。
示例 1:启动型 Service 生命周期日志
- 创建启动型 Service(StartServiceDemo):
public class StartServiceDemo extends Service {
private static final String TAG = "ServiceTest";
private Thread workThread;
private boolean isRunning = false;
// 首次创建时调用(仅1次)
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:Service创建");
// 初始化后台线程(模拟耗时操作)
initWorkThread();
}
// 每次startService时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand:接收启动请求,startId=" + startId);
// 从Intent中接收参数(示例:获取任务类型)
if (intent != null) {
String taskType = intent.getStringExtra("task_type");
Log.d(TAG, "执行任务:" + taskType);
}
// 返回值决定Service被杀死后的行为(后续详解)
return START_STICKY;
}
// 初始化后台线程(耗时操作必须放子线程)
private void initWorkThread() {
isRunning = true;
workThread = new Thread(() -> {
int count = 0;
while (isRunning) {
try {
Thread.sleep(1000);
count++;
Log.d(TAG, "后台任务执行中,计数:" + count);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
workThread.start();
}
// 绑定Service时调用(启动型Service可返回null,不支持绑定)
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind:不支持绑定,返回null");
return null;
}
// Service销毁时调用
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:Service销毁");
// 停止后台线程(避免内存泄漏)
isRunning = false;
if (workThread != null) {
workThread.interrupt();
}
}
}
- 在 Manifest 中注册 Service(必须步骤):
<!-- 注册Service,exported=false表示不允许其他应用调用 -->
<service
android:name=".StartServiceDemo"
android:exported="false" />
- 在 Activity 中启动和停止 Service:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动Service
findViewById(R.id.btn_start_service).setOnClickListener(v -> {
Intent intent = new Intent(this, StartServiceDemo.class);
intent.putExtra("task_type", "模拟文件下载");
startService(intent);
});
// 停止Service
findViewById(R.id.btn_stop_service).setOnClickListener(v -> {
Intent intent = new Intent(this, StartServiceDemo.class);
stopService(intent);
});
}
}
- 运行测试,观察 Logcat 日志:
- 首次点击 “启动 Service”:
D/ServiceTest: onCreate:Service创建
D/ServiceTest: onBind:不支持绑定,返回null
D/ServiceTest: onStartCommand:接收启动请求,startId=1
D/ServiceTest: 执行任务:模拟文件下载
D/ServiceTest: 后台任务执行中,计数:1
D/ServiceTest: 后台任务执行中,计数:2
...
- 再次点击 “启动 Service”(不停止的情况下):
D/ServiceTest: onStartCommand:接收启动请求,startId=2
D/ServiceTest: 执行任务:模拟文件下载
(不会再次调用 onCreate (),直接触发 onStartCommand ())
- 点击 “停止 Service”:
D/ServiceTest: onDestroy:Service销毁
(后台线程停止,Service 销毁)
2.3 绑定型 Service(bindService)的生命周期
核心流程:
绑定 → onCreate () → onBind () → 建立连接 → 运行中 → 解绑(unbindService) → onUnbind () → onDestroy () → 销毁
关键说明:
- 首次绑定:执行
onCreate() → onBind(); - 重复绑定:不会调用 onCreate () 和 onBind (),直接复用已建立的连接;
- 必须解绑:绑定者(如 Activity)销毁前必须调用
unbindService(),否则会导致 Service 内存泄漏; - 通信核心:onBind () 返回的
IBinder对象是组件与 Service 通信的桥梁。
示例 2:绑定型 Service 生命周期与通信
- 创建绑定型 Service(BindServiceDemo),实现 IBinder 通信:
public class BindServiceDemo extends Service {
private static final String TAG = "ServiceTest";
// 1. 创建Binder子类(通信桥梁)
private final IBinder mBinder = new LocalBinder();
// 模拟后台数据(供Activity获取)
private int mCounter = 0;
private Thread workThread;
private boolean isRunning = false;
// 2. Binder子类:暴露Service的方法给Activity
public class LocalBinder extends Binder {
// 返回Service实例,让Activity可调用Service的公共方法
BindServiceDemo getService() {
return BindServiceDemo.this;
}
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:Service创建");
// 启动后台计数任务
startCounterTask();
}
// 绑定Service时调用,返回Binder对象
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind:建立连接,返回Binder");
return mBinder;
}
// 所有绑定者解绑后调用
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind:所有绑定者解绑");
// 返回true,表示后续再次绑定会触发onRebind()
return true;
}
// 再次绑定(onUnbind返回true时)
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.d(TAG, "onRebind:重新建立连接");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:Service销毁");
// 停止后台任务
isRunning = false;
}
// 3. Service的公共方法(供Activity调用)
public int getCounter() {
return mCounter;
}
public void resetCounter() {
mCounter = 0;
}
// 后台计数任务
private void startCounterTask() {
isRunning = true;
workThread = new Thread(() -> {
while (isRunning) {
try {
Thread.sleep(1000);
mCounter++;
Log.d(TAG, "后台计数:" + mCounter);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
workThread.start();
}
}
- 在 Manifest 中注册:
<service
android:name=".BindServiceDemo"
android:exported="false" />
- 在 Activity 中绑定 Service 并通信:
public class BindServiceActivity extends AppCompatActivity {
private static final String TAG = "ServiceTest";
// 绑定服务的连接对象
private ServiceConnection mConnection;
// Service实例(通过Binder获取)
private BindServiceDemo mService;
// 是否已绑定
private boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bind_service);
// 1. 初始化ServiceConnection(监听绑定状态)
initServiceConnection();
// 绑定Service
findViewById(R.id.btn_bind).setOnClickListener(v -> {
Intent intent = new Intent(this, BindServiceDemo.class);
// 绑定Service,指定连接对象和标记(BIND_AUTO_CREATE:绑定后自动创建Service)
bindService(intent, mConnection, BIND_AUTO_CREATE);
});
// 解绑Service
findViewById(R.id.btn_unbind).setOnClickListener(v -> {
if (mBound) {
unbindService(mConnection);
mBound = false;
}
});
// 调用Service的方法(获取计数)
findViewById(R.id.btn_get_counter).setOnClickListener(v -> {
if (mBound) {
int counter = mService.getCounter();
Toast.makeText(this, "当前计数:" + counter, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "请先绑定Service", Toast.LENGTH_SHORT).show();
}
});
// 调用Service的方法(重置计数)
findViewById(R.id.btn_reset_counter).setOnClickListener(v -> {
if (mBound) {
mService.resetCounter();
Toast.makeText(this, "计数已重置", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "请先绑定Service", Toast.LENGTH_SHORT).show();
}
});
}
// 初始化ServiceConnection
private void initServiceConnection() {
mConnection = new ServiceConnection() {
// 绑定成功时触发(获取Binder对象)
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected:绑定成功");
// 转换为自定义Binder,获取Service实例
BindServiceDemo.LocalBinder binder = (BindServiceDemo.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// 绑定意外断开时触发(如Service崩溃)
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected:绑定断开");
mBound = false;
}
};
}
// Activity销毁时解绑(必须!避免内存泄漏)
@Override
protected void onDestroy() {
super.onDestroy();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
- 运行测试,观察日志:
- 点击 “绑定 Service”:
D/ServiceTest: onCreate:Service创建
D/ServiceTest: onBind:建立连接,返回Binder
D/ServiceTest: onServiceConnected:绑定成功
D/ServiceTest: 后台计数:1
D/ServiceTest: 后台计数:2
...
- 点击 “获取计数”:Toast 显示当前计数(与 Service 同步);
- 点击 “解绑 Service”:
D/ServiceTest: onUnbind:所有绑定者解绑
D/ServiceTest: onDestroy:Service销毁
- 再次绑定(已解绑后):
D/ServiceTest: onCreate:Service创建
D/ServiceTest: onBind:建立连接,返回Binder
D/ServiceTest: onServiceConnected:绑定成功
(因 onUnbind 返回 true,若再次绑定前未销毁 Service,会触发 onRebind ())
2.4 混合启动(start+bind)的生命周期
实际开发中可能需要 “启动 Service 让其持续运行,同时绑定 Service 进行通信”(如音乐播放器:startService 保持播放,bindService 控制播放 / 暂停)。
核心规则:
- 启动 + 绑定后,Service 会持续运行,需同时满足 “停止启动” 和 “解除绑定” 才会销毁;
- 生命周期流程:startService () → onCreate () → onStartCommand () → bindService () → onBind () → 运行中 → stopService () → unbindService () → onUnbind () → onDestroy ();
- 注意:必须先停止启动(stopService),再解绑(unbindService),否则 Service 可能无法正常销毁。
三、onStartCommand () 返回值:决定 Service 被杀死后的行为

启动型 Service 的onStartCommand()方法需要返回一个 int 类型值,这个返回值决定了 “当 Service 被系统杀死后,系统是否会重启它”,是 Service 保活的基础。
3.1 4 种返回值详解
| 返回值 | 核心行为 | 适用场景 |
|---|---|---|
| START_STICKY | 系统杀死后会重启 Service,但不保留之前的 Intent | 无需恢复任务(如音乐播放、定位) |
| START_NOT_STICKY | 系统杀死后不重启 Service | 一次性任务(如单次数据同步) |
| START_REDELIVER_INTENT | 系统杀死后会重启 Service,并保留最后一个 Intent | 需要恢复任务(如文件下载、断点续传) |
| START_STICKY_COMPATIBILITY | 兼容旧版本的 START_STICKY,行为不稳定 | 最低版本 < Android 2.0 时使用 |
3.2 实战示例:START_REDELIVER_INTENT 实现断点续传
场景:文件下载时 Service 被系统杀死,重启后恢复之前的下载任务。
public class DownloadService extends Service {
private static final String TAG = "DownloadService";
private String mDownloadUrl; // 下载地址(需保留)
private Thread mDownloadThread;
private boolean isDownloading = false;
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:下载Service创建");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand:启动下载任务");
// 接收下载地址(首次启动或重启后恢复)
if (intent != null) {
mDownloadUrl = intent.getStringExtra("download_url");
if (!isDownloading && mDownloadUrl != null) {
startDownload();
}
} else {
Log.d(TAG, "intent为null:可能是系统重启Service");
}
// 返回START_REDELIVER_INTENT,重启后保留最后一个Intent
return START_REDELIVER_INTENT;
}
// 模拟文件下载
private void startDownload() {
isDownloading = true;
mDownloadThread = new Thread(() -> {
try {
Log.d(TAG, "开始下载:" + mDownloadUrl);
// 模拟下载进度(10秒完成)
for (int i = 1; i <= 10; i++) {
Thread.sleep(1000);
Log.d(TAG, "下载进度:" + i + "0%");
}
Log.d(TAG, "下载完成!");
} catch (InterruptedException e) {
e.printStackTrace();
Log.d(TAG, "下载中断,等待重启恢复");
} finally {
isDownloading = false;
// 下载完成后停止Service
stopSelf();
}
});
mDownloadThread.start();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:下载Service销毁");
isDownloading = false;
if (mDownloadThread != null) {
mDownloadThread.interrupt();
}
}
}
关键说明:
- 若下载过程中 Service 被系统杀死,系统会在内存充足时重启 Service,并将最后一个 Intent 重新传入
onStartCommand(); - 重启后,
mDownloadUrl会重新赋值,startDownload()继续执行,实现 “断点续传” 效果; - 若返回
START_STICKY,重启后 Intent 为 null,无法恢复下载地址,适合无需恢复任务的场景。
四、IntentService:无需手动管理线程的 Service

前面提到,Service 默认运行在主线程,耗时操作必须手动开线程,且需在任务完成后停止 Service,容易出错。而 IntentService 是 Service 的子类,内置了子线程和任务队列,能自动处理耗时任务,且任务完成后自动销毁,无需手动管理。
4.1 IntentService 的核心优势
- 内置子线程:无需手动创建 Thread,直接在
onHandleIntent()中做耗时操作; - 任务队列:多个启动请求会按顺序执行(串行),不会并发;
- 自动销毁:所有任务执行完成后,自动调用
stopSelf()销毁 Service; - 简化代码:无需处理线程管理、停止 Service 等逻辑。
4.2 实战示例:用 IntentService 实现多任务下载
public class MyIntentService extends IntentService {
private static final String TAG = "IntentServiceTest";
// 必须实现无参构造方法(否则报错)
public MyIntentService() {
// 父类构造方法:传入线程名称
super("MyIntentServiceThread");
}
// 核心方法:运行在子线程,处理耗时任务
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
// 接收任务参数
String taskName = intent.getStringExtra("task_name");
int taskId = intent.getIntExtra("task_id", 0);
Log.d(TAG, "开始执行任务:" + taskName + "(ID:" + taskId + ")");
// 模拟耗时操作(3秒)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "任务执行完成:" + taskName + "(ID:" + taskId + ")");
}
}
// Service创建时调用
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:IntentService创建");
}
// Service销毁时调用(所有任务完成后)
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:IntentService销毁");
}
}
- 注册 Service(Manifest):
<service
android:name=".MyIntentService"
android:exported="false" />
- 在 Activity 中启动多任务:
public class IntentServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
// 启动3个任务(串行执行)
findViewById(R.id.btn_start_tasks).setOnClickListener(v -> {
startTask("下载文件A", 1);
startTask("下载文件B", 2);
startTask("下载文件C", 3);
});
}
// 启动任务
private void startTask(String taskName, int taskId) {
Intent intent = new Intent(this, MyIntentService.class);
intent.putExtra("task_name", taskName);
intent.putExtra("task_id", taskId);
startService(intent);
}
}
- 运行日志(观察串行执行和自动销毁):
D/IntentServiceTest: onCreate:IntentService创建
D/IntentServiceTest: 开始执行任务:下载文件A(ID:1)
D/IntentServiceTest: 任务执行完成:下载文件A(ID:1)
D/IntentServiceTest: 开始执行任务:下载文件B(ID:2)
D/IntentServiceTest: 任务执行完成:下载文件B(ID:2)
D/IntentServiceTest: 开始执行任务:下载文件C(ID:3)
D/IntentServiceTest: 任务执行完成:下载文件C(ID:3)
D/IntentServiceTest: onDestroy:IntentService销毁
4.3 注意事项
- IntentService 的任务是串行的,若需并发执行,需改用 Service + 多线程;
- Android 8.0 及以上,IntentService 的后台运行限制与普通 Service 一致(容易被系统杀死);
- 若需与 Activity 通信,可通过广播、EventBus 等方式(后续详解)。
五、Service 通信进阶:跨组件、跨进程通信

Service 不仅能与启动它的 Activity 通信,还能与其他组件(如 Fragment、BroadcastReceiver)、甚至其他应用(跨进程)通信。核心通信方式有 3 种:Binder(同进程)、AIDL(跨进程)、广播(简单通信)。
5.1 同进程通信:Binder + 接口回调(进阶)
前面示例中,Activity 通过 Binder 获取 Service 实例后,只能主动调用 Service 的方法。若要实现 “Service 主动向 Activity 推送数据”(如下载进度实时更新),需结合接口回调。
示例 3:Service 主动推送下载进度到 Activity
- 定义回调接口(DownloadCallback.java):
// 下载进度回调接口
public interface DownloadCallback {
void onProgressUpdate(int progress); // 进度更新
void onDownloadSuccess(String filePath); // 下载成功
void onDownloadFailed(String errorMsg); // 下载失败
}
- 改造 Service(支持注册回调):
public class ProgressService extends Service {
private static final String TAG = "ProgressServiceTest";
private LocalBinder mBinder = new LocalBinder();
private Thread mDownloadThread;
private boolean isDownloading = false;
private DownloadCallback mCallback; // 回调接口实例
// 注册回调(Activity调用)
public void setDownloadCallback(DownloadCallback callback) {
mCallback = callback;
}
// 解绑回调(避免内存泄漏)
public void removeDownloadCallback() {
mCallback = null;
}
public class LocalBinder extends Binder {
ProgressService getService() {
return ProgressService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// 开始下载(供Activity调用)
public void startDownload(String url) {
if (!isDownloading) {
isDownloading = true;
mDownloadThread = new Thread(() -> {
try {
// 模拟下载进度(0-100%,每500毫秒更新一次)
for (int progress = 0; progress <= 100; progress += 10) {
Thread.sleep(500);
// 主动回调进度(需切换到主线程,避免UI崩溃)
runOnUiThread(() -> {
if (mCallback != null) {
mCallback.onProgressUpdate(progress);
}
});
}
// 下载成功回调
runOnUiThread(() -> {
if (mCallback != null) {
mCallback.onDownloadSuccess("/sdcard/test.apk");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
// 下载失败回调
runOnUiThread(() -> {
if (mCallback != null) {
mCallback.onDownloadFailed("下载中断");
}
});
} finally {
isDownloading = false;
}
});
mDownloadThread.start();
}
}
@Override
public void onDestroy() {
super.onDestroy();
isDownloading = false;
if (mDownloadThread != null) {
mDownloadThread.interrupt();
}
removeDownloadCallback(); // 解绑回调
}
}
- Activity 实现回调接口,接收进度:
public class ProgressActivity extends AppCompatActivity implements DownloadCallback {
private ProgressService mService;
private boolean mBound = false;
private ProgressBar mProgressBar;
private TextView mTvStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress);
mProgressBar = findViewById(R.id.progress_bar);
mTvStatus = findViewById(R.id.tv_status);
// 绑定Service
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ProgressService.LocalBinder binder = (ProgressService.LocalBinder) service;
mService = binder.getService();
mBound = true;
// 注册回调
mService.setDownloadCallback(ProgressActivity.this);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
bindService(new Intent(this, ProgressService.class), connection, BIND_AUTO_CREATE);
// 开始下载
findViewById(R.id.btn_start_download).setOnClickListener(v -> {
if (mBound) {
mService.startDownload("https://example.com/test.apk");
mTvStatus.setText("下载中...");
}
});
}
// 实现回调方法(进度更新)
@Override
public void onProgressUpdate(int progress) {
mProgressBar.setProgress(progress);
mTvStatus.setText("下载进度:" + progress + "%");
}
// 实现回调方法(下载成功)
@Override
public void onDownloadSuccess(String filePath) {
mTvStatus.setText("下载成功:" + filePath);
}
// 实现回调方法(下载失败)
@Override
public void onDownloadFailed(String errorMsg) {
mTvStatus.setText("下载失败:" + errorMsg);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBound) {
// 解绑回调和Service
mService.removeDownloadCallback();
unbindService(new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {}
@Override
public void onServiceDisconnected(ComponentName name) {}
});
}
}
}
5.2 跨进程通信:AIDL(Android 接口定义语言)
当需要 “两个不同应用” 或 “同一应用的不同进程” 通信时,Binder 无法直接使用,需通过 AIDL 实现。AIDL 的核心是定义跨进程通信的接口,系统会自动生成通信相关的代码。
示例 4:用 AIDL 实现跨进程获取服务状态
步骤 1:创建 AIDL 文件(定义通信接口)
- 在
main目录下新建aidl文件夹(与java同级); - 在 aidl 文件夹下创建包名(与 Java 代码包名一致,如
com.example.service); - 新建 AIDL 文件(IMyAidlInterface.aidl):
// IMyAidlInterface.aidl
package com.example.service;
// 定义跨进程通信的接口
interface IMyAidlInterface {
// 获取服务状态(示例方法)
String getServiceStatus();
// 启动服务任务
void startServiceTask(String taskName);
}
- 点击 Android Studio 的 “Sync Project with Gradle Files”,系统会自动生成 Java 代码(在
build/generated/aidl目录下)。
步骤 2:实现 AIDL 接口(Service 端)
public class AIDLService extends Service {
private static final String TAG = "AIDLServiceTest";
private boolean isTaskRunning = false;
// 实现AIDL接口(自动生成的Stub类)
private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public String getServiceStatus() throws RemoteException {
// 跨进程调用时执行此方法(运行在Binder线程池)
return isTaskRunning ? "服务正在执行任务" : "服务空闲";
}
@Override
public void startServiceTask(String taskName) throws RemoteException {
Log.d(TAG, "跨进程启动任务:" + taskName);
isTaskRunning = true;
// 模拟任务执行(3秒后结束)
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isTaskRunning = false;
}).start();
}
};
@Override
public IBinder onBind(Intent intent) {
// 返回Stub实例(跨进程通信的桥梁)
return mBinder;
}
}
步骤 3:注册 Service(允许跨进程调用)
<service
android:name=".AIDLService"
android:exported="true"> <!-- exported=true:允许其他应用调用 -->
<intent-filter>
<!-- 隐式启动的Action(供其他应用匹配) -->
<action android:name="com.example.service.AIDL_SERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
步骤 4:客户端(另一个应用)调用 AIDL 服务
- 复制 AIDL 文件到客户端应用(包名必须与服务端一致);
- 同步 Gradle,生成 Java 代码;
- 客户端绑定服务并调用接口:
public class AIDLClientActivity extends AppCompatActivity {
private IMyAidlInterface mAidlInterface;
private boolean mBound = false;
private TextView mTvStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl_client);
mTvStatus = findViewById(R.id.tv_status);
// 绑定服务端的AIDLService
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 转换为AIDL接口实例
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
mBound = true;
mTvStatus.setText("绑定成功");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mAidlInterface = null;
mBound = false;
mTvStatus.setText("绑定断开");
}
};
Intent intent = new Intent();
// 设置服务端的Action(与Manifest一致)
intent.setAction("com.example.service.AIDL_SERVICE");
// 跨应用绑定需设置包名(Android 11及以上必须)
intent.setPackage("com.example.service"); // 服务端应用包名
bindService(intent, connection, BIND_AUTO_CREATE);
// 获取服务状态
findViewById(R.id.btn_get_status).setOnClickListener(v -> {
if (mBound && mAidlInterface != null) {
try {
String status = mAidlInterface.getServiceStatus();
mTvStatus.setText("服务状态:" + status);
} catch (RemoteException e) {
e.printStackTrace();
mTvStatus.setText("获取状态失败");
}
}
});
// 启动服务任务
findViewById(R.id.btn_start_task).setOnClickListener(v -> {
if (mBound && mAidlInterface != null) {
try {
mAidlInterface.startServiceTask("跨进程测试任务");
mTvStatus.setText("任务已启动");
} catch (RemoteException e) {
e.printStackTrace();
mTvStatus.setText("启动任务失败");
}
}
});
}
}
5.3 简单通信:广播(适合轻量数据传递)
若通信需求简单(如 Service 完成任务后通知 Activity),可使用广播,无需绑定机制,实现更简单。
示例 5:Service 通过广播通知 Activity 任务完成
- Service 发送广播:
public class BroadcastService extends Service {
private static final String TAG = "BroadcastServiceTest";
// 自定义广播Action
public static final String ACTION_TASK_COMPLETE = "com.example.service.TASK_COMPLETE";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 模拟耗时任务
new Thread(() -> {
try {
Thread.sleep(3000);
// 任务完成,发送广播
Intent broadcastIntent = new Intent(ACTION_TASK_COMPLETE);
broadcastIntent.putExtra("result", "任务执行成功");
// 发送本地广播(仅本应用接收,更安全)
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
stopSelf();
}
}).start();
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
- Activity 接收广播:
public class BroadcastActivity extends AppCompatActivity {
private LocalBroadcastManager mLocalBroadcastManager;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BroadcastService.ACTION_TASK_COMPLETE)) {
String result = intent.getStringExtra("result");
Toast.makeText(context, result, Toast.LENGTH_SHORT).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast);
// 初始化本地广播管理器
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
// 注册广播接收器
IntentFilter filter = new IntentFilter();
filter.addAction(BroadcastService.ACTION_TASK_COMPLETE);
mLocalBroadcastManager.registerReceiver(mReceiver, filter);
// 启动Service
findViewById(R.id.btn_start_service).setOnClickListener(v -> {
startService(new Intent(this, BroadcastService.class));
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销广播(避免内存泄漏)
mLocalBroadcastManager.unregisterReceiver(mReceiver);
}
}
5.4 3 种通信方式对比
| 通信方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Binder + 回调 | 同进程、需双向通信 | 效率高、灵活 | 需绑定,代码稍复杂 |
| AIDL | 跨进程、复杂通信 | 支持多进程交互 | 代码复杂,需处理 RemoteException |
| 广播 | 同 / 跨进程、简单通知 | 无需绑定,实现简单 | 效率低,不适合大量数据传递 |
六、Service 保活方案:避免被系统杀死的实战技巧

Android 系统为了优化内存,会在内存不足时回收后台 Service,这对需要持续运行的场景(如音乐播放、定位追踪)是致命的。但保活需 “合法合规”,避免使用违规方案(如双 Service 互相唤醒、后台弹出 Activity),否则会被应用商店拒审。
6.1 合法保活方案(推荐)
方案 1:前台服务(Foreground Service)
前台服务会在系统状态栏显示通知,优先级更高,几乎不会被系统回收(除非内存极度紧张)。适用于需要用户感知的后台任务(如音乐播放、导航)。
示例 6:实现前台服务
public class ForegroundService extends Service {
private static final String TAG = "ForegroundServiceTest";
private static final int NOTIFICATION_ID = 1001; // 通知ID(必须唯一)
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:前台服务创建");
// 启动前台服务(必须显示通知)
startForeground(NOTIFICATION_ID, createNotification());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 模拟后台任务(如音乐播放)
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
Log.d(TAG, "前台服务运行中...");
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}).start();
return START_STICKY;
}
// 创建前台服务通知
private Notification createNotification() {
// Android 8.0及以上需创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"foreground_channel", // 渠道ID
"前台服务通知", // 渠道名称
NotificationManager.IMPORTANCE_DEFAULT // 重要性
);
// 注册通知渠道
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
// 构建通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "foreground_channel")
.setSmallIcon(R.mipmap.ic_launcher) // 通知图标(必须)
.setContentTitle("前台服务") // 通知标题
.setContentText("服务正在后台运行...") // 通知内容
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
// 设置通知点击事件(可选)
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE
);
builder.setContentIntent(pendingIntent);
return builder.build();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:前台服务销毁");
// 停止前台服务(移除通知)
stopForeground(true);
}
}
关键说明:
- Android 8.0 及以上必须创建通知渠道,否则通知无法显示,前台服务启动失败;
- 前台服务的通知不能手动取消(除非服务停止),需通过
stopForeground(true)移除; - 需在 Manifest 中添加前台服务权限(Android 9.0 及以上):
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
方案 2:WorkManager(替代后台 Service,Android 8.0 + 推荐)
Android 8.0(API 26)后,系统对后台 Service 限制加强(后台 Service 容易被杀死),Google 推荐用 WorkManager 替代 “非即时性后台任务”(如数据同步、文件备份、周期性任务)。
WorkManager 的优势:
- 兼容所有 Android 版本(API 14+);
- 自动适配系统限制,在后台安全执行任务;
- 支持周期性任务、延迟任务、网络条件触发等;
- 任务可持久化,设备重启后自动恢复。
示例 7:用 WorkManager 实现周期性数据同步
- 添加依赖(AndroidX):
implementation 'androidx.work:work-runtime:2.8.1'
- 创建 Worker(任务执行类):
public class SyncWorker extends Worker {
private static final String TAG = "SyncWorkerTest";
public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
// 核心方法:执行后台任务(运行在子线程)
@NonNull
@Override
public Result doWork() {
Log.d(TAG, "开始数据同步...");
try {
// 模拟数据同步(2秒)
Thread.sleep(2000);
Log.d(TAG, "数据同步完成!");
return Result.success(); // 任务成功
} catch (InterruptedException e) {
e.printStackTrace();
return Result.retry(); // 任务重试(可设置重试策略)
}
}
}
- 在 Activity 中调度任务:
public class WorkManagerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_work_manager);
// 调度周期性任务(每15分钟执行一次,最小周期为15分钟)
findViewById(R.id.btn_schedule_sync).setOnClickListener(v -> {
// 1. 配置任务参数(周期性任务)
PeriodicWorkRequest syncRequest = new PeriodicWorkRequest.Builder(
SyncWorker.class,
15, TimeUnit.MINUTES // 周期15分钟
)
// 设置约束条件(如网络可用时执行)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build();
// 2. 提交任务(唯一名称,重复提交会替换)
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork(
"data_sync_work",
ExistingPeriodicWorkPolicy.REPLACE, // 重复时替换
syncRequest
);
Toast.makeText(this, "任务调度成功", Toast.LENGTH_SHORT).show();
});
// 取消任务
findViewById(R.id.btn_cancel_sync).setOnClickListener(v -> {
WorkManager.getInstance(this).cancelUniqueWork("data_sync_work");
Toast.makeText(this, "任务已取消", Toast.LENGTH_SHORT).show();
});
}
}
6.2 灰色保活方案(谨慎使用)
以下方案适用于特殊场景(如企业级应用、工具类应用),普通应用不建议使用,可能影响用户体验或被应用商店拒审:
- 方案 3:1 像素 Activity 唤醒:Service 被杀死前,启动一个透明的 1 像素 Activity,让应用回到前台,避免被回收;
- 方案 4:广播唤醒:注册系统广播(如屏幕亮灭、网络变化),收到广播后重启 Service;
- 方案 5:双进程守护:创建两个进程的 Service,互相监听,一个被杀死后,另一个重启它。
七、Service 常见坑点与避坑指南(实战经验总结)

7.1 坑点 1:Service 主线程耗时导致 ANR
- 现象:在 Service 的 onCreate ()、onStartCommand () 中做耗时操作(如网络请求、文件读写),导致应用无响应(ANR);
- 原因:Service 默认运行在主线程,主线程阻塞超过 5 秒会触发 ANR;
- 解决方案:
- 所有耗时操作必须放在子线程(Thread、AsyncTask、Coroutine);
- 推荐用 IntentService 或 WorkManager,无需手动管理线程;
- 示例(正确的耗时操作):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 正确:耗时操作放子线程
new Thread(() -> {
// 执行网络请求、文件下载等操作
}).start();
return START_STICKY;
}
7.2 坑点 2:绑定 Service 后未解绑导致内存泄漏
- 现象:Activity 绑定 Service 后,未调用 unbindService () 就销毁,导致 Service 无法回收,进而造成内存泄漏;
- 原因:绑定后,Service 会持有 Activity 的引用,Activity 销毁时引用未释放;
- 解决方案:
- 在 Activity 的 onDestroy () 中强制解绑(判断是否已绑定);
- 避免在非 Activity 组件(如 Application)中绑定 Service;
- 示例(正确的解绑逻辑):
@Override
protected void onDestroy() {
super.onDestroy();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
7.3 坑点 3:Android 8.0 + 后台 Service 容易被杀死
- 现象:Android 8.0 及以上,应用退到后台后,Service 很快被系统杀死;
- 原因:Android 8.0 引入 “后台服务限制”,后台应用的 Service 会被系统限制运行;
- 解决方案:
- 即时性任务:用前台服务(Foreground Service);
- 非即时性任务:用 WorkManager 替代;
- 避免在后台 Service 中执行长时间运行的任务。
7.4 坑点 4:IntentService 并发任务执行失败
- 现象:向 IntentService 发送多个任务,期望并发执行,但实际是串行执行;
- 原因:IntentService 的任务队列是串行的,多个任务按顺序执行,无法并发;
- 解决方案:
- 需并发执行的任务:改用 Service + 多线程(如线程池);
- 示例(Service + 线程池实现并发):
public class ConcurrentService extends Service {
// 线程池(3个核心线程)
private ExecutorService mExecutor = Executors.newFixedThreadPool(3);
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String taskName = intent.getStringExtra("task_name");
// 提交任务到线程池(并发执行)
mExecutor.submit(() -> {
Log.d(TAG, "执行任务:" + taskName + ",线程:" + Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
mExecutor.shutdown(); // 关闭线程池
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
7.5 坑点 5:跨进程通信时抛出 RemoteException
- 现象:AIDL 跨进程通信时,调用接口方法抛出 RemoteException;
- 原因:跨进程通信可能因网络异常、服务端崩溃等导致通信失败;
- 解决方案:
- 所有 AIDL 接口调用必须放在 try-catch 块中;
- 通信前检查服务是否已绑定;
- 示例:
try {
if (mAidlInterface != null) {
String status = mAidlInterface.getServiceStatus();
}
} catch (RemoteException e) {
e.printStackTrace();
Toast.makeText(this, "通信失败,请重试", Toast.LENGTH_SHORT).show();
}
八、总结:Service 核心知识点图谱
到这里,Service 的核心内容已经全部讲完,我们用一张图谱梳理重点:
Service核心知识点
├── 基础认知:后台任务容器、与Thread/IntentService的区别
├── 生命周期:启动型(startService)、绑定型(bindService)、混合启动
├── onStartCommand返回值:4种类型(决定被杀死后的行为)
├── 关键子类:IntentService(自动管理线程和销毁)
├── 通信方式:Binder+回调(同进程)、AIDL(跨进程)、广播(简单通信)
├── 保活方案:前台服务(合法)、WorkManager(Android 8.0+推荐)、灰色方案(谨慎使用)
├── 避坑指南:ANR、内存泄漏、后台限制、并发问题、跨进程异常
其实 Service 的学习关键是 “理解生命周期 + 区分使用场景”—— 启动型 Service 适合独立后台任务,绑定型 Service 适合组件通信,IntentService 适合异步单次任务,WorkManager 适合非即时性后台任务。
1518

被折叠的 条评论
为什么被折叠?



