簡介
在 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
?的服務,系統會在資源允許時嘗試重啟被殺死服務。
- 通過?