在安卓应用开发中,用户界面的交互往往只是冰山一角,大量的后台任务,如音乐播放、文件下载、数据同步等,都需要在不影响用户操作界面的情况下持续运行。这时,安卓四大组件之一的Service就派上了用场。
一、Service 的基本概念
- Service 是 Android 系统中无可视化界面、运行于后台的长生命周期组件。
- 核心功能:执行与用户界面无关的持续性任务,如后台播放音乐、文件下载等。
- Service 不依赖用户交互界面,生命周期独立于Activity。
- 典型应用场景:网络请求、传感器数据采集、跨进程通信(AIDL)
二、生命周期管理
Service有两种主要类型:
特性 | Started Service(启动式) | Bound Service(绑定式) |
启动方式 | 通过startService(Intent)启动 | 通过bindService(Intent,ServiceConnection,flags)启动 |
生命周期 | onCreate()→onStartCommand()→onDestroy() | onCreate()→onBind()→onUnbind()→onDestroy() |
通信机制 | 无法直接与组件交互,需要通过广播或者Intent传递数据 | 通过Binder接口直接通信(支持方法调用) |
销毁条件 | 需手动调用stopSelf()或着stopService() | 所有绑定组件解绑后接口自动销毁 |
多组件绑定 | 不支持,每次启动独立运行 | 支持与多个组件同事绑定(如多个Activity共享同一服务实例) |
适用场景 | 一次性后台任务(如下载,音乐播放) | 长期交互服务(如数据同步、实时计算) |
优先级与系统回收 | 后台服务可能会被系统回收,可通过startForeground()提升为前台服务 |
优先级较低,绑定组件退出后可能更快被回收 |
共存场景 | 可与Bound Service共存,需同时调用stopSelf()和解绑操作才能销毁 | 与Start Service共存时,需先解绑所有组件再手动停止服务 |
涉及的生命周期方法:
生命周期方法 | 出发场景 |
onCreate() | Service首次创建时调用(仅一次) |
onStartCommand() | 每次通过startService()启动时调用 |
onBind() | 通过bindService()绑定时调用 |
onUnbind() | 所有客户端解绑时调用 |
onDestroy | Service被销毁前调用(需手动停止或系统回收) |
Service 默认运行在主线程,耗时操作需自行创建子线程或使用
IntentService
三、启动和停止Service
1. 定义 Service 类
继承 Service
类并实现核心方法:
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public IBinder onBind(Intent intent) {
return null; // 非绑定模式时返回 null
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service 启动"); // 执行后台任务
return START_STICKY; // 服务终止后自动重启
}
@Override
public void onDestroy() {
Log.d(TAG, "Service 销毁");
super.onDestroy();
}
}
2. 注册 Service
在 AndroidManifest.xml
中添加声明:
<application>
<service android:name=".MyService" />
</application>
3. 通过 startService()
启动
在 Activity 或其他组件中调用:
Intent serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent); // 启动服务:ml-citation{ref="6,8" data="citationList"}
4. 通过 stopService()
或 stopSelf()
停止
stopService(new Intent(this, MyService.class)); // 外部停止
// 或在 Service 内部调用 stopSelf();:ml-citation{ref="6,8" data="citationList"}
四、绑定Service
1. 定义 Bound Service
- 通过
LocalBinder
返回 Service 实例,实现组件间交互 onBind()
返回IBinder
对象,供客户端绑定
// MyBoundService.java
public class MyBoundService extends Service {
private final IBinder binder = new LocalBinder();
private static final String TAG = "MyBoundService";
public class LocalBinder extends Binder {
MyBoundService getService() {
return MyBoundService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Service 已绑定");
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "所有客户端已解绑");
return super.onUnbind(intent);
}
// 自定义服务方法(供Activity调用)
public void performTask(String data) {
Log.d(TAG, "执行任务:" + data);
}
}
2. 注册 Service
在 AndroidManifest.xml
中添加声明:
<application>
<service android:name=".MyBoundService" />
</application>
3. Activity 绑定与通信
- 通过 bindService() 建立绑定
- ServiceConnection 处理绑定成功/断开事件
- 绑定后通过 myService 实例直接调用服务方法
- 必须调用 unbindService() 释放资源,避免内存泄漏
- 多个组件可绑定同一服务,全部解绑后服务销毁
// MainActivity.java
public class MainActivity extends AppCompatActivity {
private MyBoundService myService;
private boolean isBound = false;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyBoundService.LocalBinder binder
= (MyBoundService.LocalBinder) service;
myService = binder.getService();
isBound = true;
myService.performTask("Hello from Activity!"); // 调用服务方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定服务
Intent intent = new Intent(this, MyBoundService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isBound) {
unbindService(connection); // 必须解绑避免泄漏
isBound = false;
}
}
}
Activity 创建时: bindService() → Service: onCreate() → onBind()
Activity 销毁时: unbindService() → Service: onUnbind() → onDestroy()
四、前台服务
前台Service具有较高的优先级,会在状态栏显示一个通知,告知用户该Service正在运行,以防止被系统轻易终止。常见使用场景包括:
- 音乐播放:在状态栏显示播放控制通知,用户可随时暂停、切换歌曲。
- 文件下载:实时展示下载进度,避免下载任务被系统回收。
- Android 8.0+ 必须创建 NotificationChannel,否则通知无法显示
- 通过 IMPORTANCE_LOW 设置低优先级(无提示音)
- startForeground() 必须在 onCreate() 或 onStartCommand() 中调用,调用后服务优先级提升,避免被系统轻易回收
- stopForeground(true) 确保通知栏通知被移除
// ForegroundService.java
public class ForegroundService extends Service {
private static final int NOTIFICATION_ID = 1001;
private static final String CHANNEL_ID = "foreground_service_channel";
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
startForegroundWithNotification("服务初始化中...");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 获取 Activity 传递的数据(可选)
String inputData = intent != null
? intent.getStringExtra("input_data") : null;
updateNotification("正在运行: " + inputData);
// 模拟耗时任务
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
updateNotification("进度: " + (i + 1) * 10 + "%");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stopSelf(); // 任务完成后自动停止服务
}).start();
return START_STICKY; // 服务被系统杀死后自动重启
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"前台服务示例",
NotificationManager.IMPORTANCE_LOW
);
channel.setDescription("用于展示前台服务的持续运行状态");
NotificationManager manager
= getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
private void startForegroundWithNotification(String text) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
notificationIntent,
PendingIntent.FLAG_IMMUTABLE
);
Notification notification = new NotificationCompat
.Builder(this, CHANNEL_ID)
.setContentTitle("前台服务示例")
.setContentText(text)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pendingIntent)
.setOnlyAlertOnce(true) // 避免重复提示音
.build();
startForeground(NOTIFICATION_ID, notification);
}
private void updateNotification(String text) {
Notification notification = new NotificationCompat
.Builder(this, CHANNEL_ID)
.setContentTitle("前台服务示例")
.setContentText(text)
.setSmallIcon(R.drawable.ic_notification)
.setOnlyAlertOnce(true)
.build();
NotificationManager manager
= getSystemService(NotificationManager.class);
manager.notify(NOTIFICATION_ID, notification);
}
@Override
public IBinder onBind(Intent intent) {
return null; // 无需绑定功能
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true); // 停止时移除通知
}
}
2. 配置清单文件注册服务
<!-- 服务端 AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 必须的权限声明,否则startForeground方法不可用 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application>
<!-- 服务定义 -->
<service
android:name=".ForegroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaPlayback"/> <!-- 按需配置类型 -->
</application>
</manifest>
3. 客户端调用(Activity)
- 通过
stopSelf()
或stopService()
停止服务
// MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动前台服务按钮
findViewById(R.id.btn_start).setOnClickListener(v -> {
Intent serviceIntent = new Intent(this, ForegroundService.class);
serviceIntent.putExtra("input_data", "用户启动任务");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent); // Android 8.0+ 专用方法
} else {
startService(serviceIntent);
}
});
// 停止服务按钮
findViewById(R.id.btn_stop).setOnClickListener(v -> {
Intent serviceIntent = new Intent(this, ForegroundService.class);
stopService(serviceIntent);
});
}
}
4. 客户端配置权限
<!-- 客户端 AndroidManifest.xml -->
<manifest>
<!-- 添加权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- 跨进程调用时添加(可选) -->
<queries>
<package android:name="com.example.service"/>
</queries>
<application>
<!-- 无需声明服务组件 -->
</application>
</manifest>
五、服务启动方法混用
1. startService()
重复调用
- 首次调用:触发
onCreate()
→onStartCommand()
- 后续调用:仅触发
onStartCommand()
,onCreate()
不再执行
// 第一次调用
startService(intent); // onCreate() -> onStartCommand()
// 第二次调用
startService(intent); // 仅 onStartCommand()
2. bindService()
重复调用
- 首次绑定:触发
onCreate()
→onBind()
- 后续绑定:若 Service 已存在,直接返回已创建的
IBinder
对象,不再触发onBind()
// 第一次绑定
// onCreate() -> onBind()
bindService(intent, conn, BIND_AUTO_CREATE);
// 第二次绑定(同一进程)
// 无生命周期方法调用,复用已有 IBinder
bindService(intent, conn2, BIND_AUTO_CREATE);
不同进程再次绑定:
同一 Service 实例:若 Service 已在独立进程运行,后续绑定直接复用已有实例,不再触发 onCreate() 和 onBind(),仅通过 ServiceConnection 返回 IBinder 代理对象。
新 Service 实例:若应用配置多进程且未声明 android:process,不同组件进程可能触发多个 Service 实例(需避免此设计)
3. 混合调用场景
startService()
后 bindService():
Service 生命周期持续到 unbindService()
和 stopService()
/stopSelf()
均被调用
startService(intent); // onCreate() -> onStartCommand()
bindService(intent, conn); // onBind(),Service 已存在无需创建
同理,先bindService()后startService()
bindService(intent, conn); // 创建 Service 实例,onCreate() → onBind()
startService(intent); // onStartCommand(),Service 已存在无需创建
混合调用时需同时调用
stopService()
和unbindService()
才能销毁 Service
六、Service 与其他组件的交互
1. Service 与 Activity 通信
- 启动式通信:Activity通过startService()启动Service,并传递参数(如Intent.putExtra()),但难以获取Service的执行结果。
- 绑定式通信:Activity通过bindService()绑定Service,获取IBinder对象后,可调用Service中的公共方法,实现双向通信。
2. Service 与 BroadcastReceiver 联动
Service可通过sendBroadcast()发送广播,由BroadcastReceiver接收并处理;反之,BroadcastReceiver也可在接收到特定广播后启动Service,例如,当设备网络状态改变时,BroadcastReceiver启动Service进行数据同步。
七、Service 使用的注意事项
- 避免在主线程执行耗时操作:Service默认运行在主线程,若执行耗时任务(如网络请求),需开启子线程,否则会导致应用 ANR(Application Not Responding)。
- 内存管理:及时释放Service占用的资源,避免内存泄漏。
- 适配高版本安卓系统:关注安卓系统对Service的限制和优化,如安卓 10(API 级别 29)进一步限制后台服务的使用,需采用合适的替代方案(如 WorkManager)