学习 Android(十一)Service
简介
在 Android 中,Service 是一种无界面的组件,用于在后台执行长期运行或跨进程的任务,如播放音乐、网络下载或与远程服务通信 。Service 可分为“启动型(Started)”和“绑定型(Bound)”两大类,它们在生命周期管理、调用方式和使用场景上各具特色 。下面将详述 Service 的类型与生命周期,并通过示例演示其特点与运行流程。
1. Service 的基本概念
-
什么是 Service
Service
是 Android 四大组件之一(另包括Activity
、BroadcastReceiver
、ContentProvider
),它允许应用在后台执行操作,即使切换到其他应用也能继续运行。
Service 没有用户界面,主要用于执行不需要用户直接交互的任务,如播放音乐、上传文件、定时同步等。 -
Service 与 Activity 的区别
- 可见性:
Activity
有界面并位于前台;Service
无界面,在后台运行。 - 生命周期管理:Activity 生命周期由用户导航驱动;Service 生命周期由调用组件或绑定状态驱动。
- 线程模型:默认在主线程执行,需要手动创建子线程以避免阻塞 UI。
- 可见性:
2. Service 的类型
-
启动型 Service (Started Service)
-
启动方式:通过
startService(Intent)
调用。 -
生命周期:调用
onCreate()
→onStartCommand()
→ (任务完成后)stopSelf()
或由外部stopService()
停止 →onDestroy()
。 -
特点:服务一旦启动,即使启动它的组件销毁也会继续运行,适合单次或周期性后台任务。
-
-
绑定型 Service (Bound Service)
-
启动方式:通过
bindService(Intent, ServiceConnection, flags)
调用。 -
生命周期:
onCreate()
→onBind()
→ 与客户端保持连接 → 客户端unbindService()
后 →onUnbind()
→onDestroy()
。 -
特点:提供客户端-服务端接口,允许 Activity 或其他组件调用服务方法并获取返回结果,通常用于进程间通信(IPC)。
-
-
前台 Service (Foreground Service)
- 在 Android O 及以上,需要在启动时调用
startForeground()
并显示持续通知,保证系统不会轻易回收。 - 适用场景:音乐播放、导航、健康监测等用户可见的重要服务。
- 在 Android O 及以上,需要在启动时调用
3. Service 的生命周期
Android 官方将 Service 的生命周期分为两条主路径:Started 和 Bound
-
启动型 Service 生命周期
onCreate()↓ onStartCommand()↓ (可多次调用 onStartCommand) stopService() 或 stopSelf()↓ onDestroy()
- onCreate():首次创建时调用,用于初始化资源。
- onStartCommand():每次
startService()
调用后执行,返回值决定系统在被杀后如何重启服务(START_STICKY
、START_NOT_STICKY
等)。 - onDestroy():在
stopSelf()
或stopService()
后执行,释放资源。
-
绑定型 Service 生命周期
onCreate()↓ onBind()↓ (可多次 bind/unbind) onUnbind()↓ onDestroy()
- onBind():客户端绑定时调用,返回
IBinder
用于客户端调用服务方法。 - onUnbind():最后一个客户端解绑时调用,可决定是否再次允许绑定(返回
true
重写onRebind()
)。
- onBind():客户端绑定时调用,返回
4. Service 生命周期示例
-
MyService
class MyService : Service() {private val tag = "ServiceLifecycle"private val binder = LocalBinder()// 用于绑定的 Binder 实现inner class LocalBinder : Binder() {fun getService(): MyService = this@MyService}override fun onCreate() {super.onCreate()Log.d(tag, "onCreate() called")}override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {Log.d(tag, "onStartCommand() called with startId: $startId")return START_STICKY}override fun onBind(intent: Intent): IBinder {Log.d(tag, "onBind() called")return binder}override fun onUnbind(intent: Intent?): Boolean {Log.d(tag, "onUnbind() called")return super.onUnbind(intent)}override fun onRebind(intent: Intent?) {super.onRebind(intent)Log.d(tag, "onRebind() called")}override fun onDestroy() {super.onDestroy()Log.d(tag, "onDestroy() called")}fun exampleMethod() {Log.d(tag, "Custom method called")} }
记得要在
AndroidManifest.xml
文件中声明<serviceandroid:name=".MyService"android:enabled="true"android:exported="true" />
-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><Buttonandroid:id="@+id/btn_start_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Start Service" /><Buttonandroid:id="@+id/btn_stop_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Stop Service" /><Buttonandroid:id="@+id/btn_bind_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Bind Service" /><Buttonandroid:id="@+id/btn_un_bind_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="UnBind Service" /> </LinearLayout>
-
MainActivity
import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.IBinder import android.util.Log import android.widget.Buttonclass MainActivity : AppCompatActivity() {private val tag = "ServiceLifecycle"private var service: MyService? = nullprivate var isBound = falseprivate val btnStartService: Button by lazy { findViewById<Button>(R.id.btn_start_service) }private val btnStopService: Button by lazy { findViewById<Button>(R.id.btn_stop_service) }private val btnBindService: Button by lazy { findViewById<Button>(R.id.btn_bind_service) }private val btnUnbindService: Button by lazy { findViewById<Button>(R.id.btn_un_bind_service) }private val connection = object : ServiceConnection {override fun onServiceConnected(className: ComponentName, binder: IBinder) {Log.d(tag, "ServiceConnection.onServiceConnected()")val localBinder = binder as MyService.LocalBinderservice = localBinder.getService()isBound = trueservice?.exampleMethod()}override fun onServiceDisconnected(arg0: ComponentName) {Log.d(tag, "ServiceConnection.onServiceDisconnected()")isBound = false}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)Log.d(tag, "Activity onCreate()")// 按钮点击监听btnStartService.setOnClickListener {val intent = Intent(this, MyService::class.java)startService(intent)}btnStopService.setOnClickListener {val intent = Intent(this, MyService::class.java)stopService(intent)}btnBindService.setOnClickListener {val intent = Intent(this, MyService::class.java)bindService(intent, connection, Context.BIND_AUTO_CREATE)}btnUnbindService.setOnClickListener {if (isBound) {unbindService(connection)isBound = false}}}override fun onDestroy() {super.onDestroy()Log.d(tag, "Activity onDestroy()")} }
-
点击
Start Service
后,再点击Stop Service
onCreate() called onStartCommand() called with startId: 1onDestroy() called
-
点击
Bind Service
后,再点击Unbind Service
onCreate() called onBind() called ServiceConnection.onServiceConnected() Custom method calledonUnbind() called onDestroy() called
-
点击
Start Service
后,再点击Bind Service
后, 再点击Stop Service
后,再点击UnBind Service
点击 Start Service onCreate() called onStartCommand() called with startId: 1点击 Bind Service onBind() called ServiceConnection.onServiceConnected() Custom method called点击 Stop Service 无点击 Unbind Service onUnbind() called
-
5. Service 常见面试题
-
什么是 Service?它与 Thread 的区别是什么?
答案:
Service 是 Android 的四大组件之一,用于在后台执行长时间运行的操作(如音乐播放、网络请求),无需用户界面。
与 Thread 的区别:
-
Service 默认运行在主线程中,需要手动创建子线程处理耗时操作;Thread 是并发执行的基本单位。
-
Service 是系统组件,由系统管理生命周期;Thread 需开发者自行管理生命周期。
-
-
Service 的两种启动方式及区别?
答案:
-
startService()
:通过Intent
启动,Service 会一直运行直到调用stopSelf()
或外部调用stopService()
。适用于独立后台任务(如下载文件)。 -
bindService()
:通过ServiceConnection
绑定,Service 生命周期与绑定组件(如 Activity)关联。适用于交互场景(如音乐控制)。
-
-
描述 Service 的生命周期方法(分启动和绑定两种情况)
答案:
-
仅
startService()
:onCreate()
→onStartCommand()
→running
→stopSelf()
→onDestroy()
。 -
仅
bindService()
:onCreate()
→onBind()
→running
→onUnbind()
→onDestroy()
。 -
混合启动(先
startService()
再bindService()
):需同时调用stopService()
和unbindService()
才会销毁。
-
-
onStartCommand() 的返回值有何意义?
答案:
返回值决定 Service 被系统杀死后的行为:
-
START_STICKY
:系统重建 Service,但不保留 Intent。 -
START_NOT_STICKY
:系统不主动重建。 -
START_REDELIVER_INTENT
:系统重建 Service 并重新传递最后的 Intent。
-
-
什么是前台服务?如何实现?
答案:
-
前台服务:需显示通知(如音乐播放器),避免被系统杀死。
-
实现:调用
startForeground(int id, Notification notification)
,需声明FOREGROUND_SERVICE
权限。
-
-
IntentService 的特点是什么?为什么被弃用?
答案:
-
特点:自动在子线程处理任务,任务完成后自动销毁。
-
弃用原因:Android 8.0(API 26)后限制后台服务,推荐使用
JobIntentService
或WorkManager
。
-
-
如何保证 Service 不被杀死?
答案:
-
使用前台服务并显示通知。
-
onStartCommand()
返回START_STICKY
。 -
在
onDestroy()
中重启 Service(不推荐,影响用户体验)。
-
-
Service 与 Activity 如何通信?
答案:
-
方式 1:通过
Binder
(绑定服务时返回自定义 Binder 对象)。 -
方式 2:使用
BroadcastReceiver
或LiveData
(解耦场景)。 -
方式 3:
Messenger
(跨进程通信)。
-
-
Android 8.0 后如何替代后台服务?
答案:
- 使用
JobScheduler
、WorkManager
(兼容性更好)或前台服务。后台执行限制旨在优化电池寿命。
- 使用
-
音乐播放器为何用 Service 而不用 Thread?
答案:
- Service 作为独立组件,生命周期不受 Activity 影响(如退出界面仍可播放)。Thread 随 Activity 销毁可能被终止,且无法直接跨界面控制。
-
什么是粘性服务(Sticky Service)?
答案:
- 通过
startService()
启动且onStartCommand()
返回START_STICKY
的服务,系统会在资源允许时尝试重启被杀死服务。
- 通过