学习 Android(十一)Service

简介

在 Android 中,Service 是一种无界面的组件,用于在后台执行长期运行跨进程的任务,如播放音乐、网络下载或与远程服务通信 。Service 可分为“启动型(Started)”和“绑定型(Bound)”两大类,它们在生命周期管理、调用方式和使用场景上各具特色 。下面将详述 Service 的类型与生命周期,并通过示例演示其特点与运行流程。

1. Service 的基本概念

  • 什么是 Service

    Service 是 Android 四大组件之一(另包括 ActivityBroadcastReceiverContentProvider),它允许应用在后台执行操作,即使切换到其他应用也能继续运行。
    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() 并显示持续通知,保证系统不会轻易回收。
    • 适用场景:音乐播放、导航、健康监测等用户可见的重要服务。

3. Service 的生命周期

Android 官方将 Service 的生命周期分为两条主路径:StartedBound

  • 启动型 Service 生命周期

    onCreate()
       ↓
    onStartCommand()
       ↓  (可多次调用 onStartCommand)
    stopService() 或 stopSelf()
       ↓
    onDestroy()
    
    • onCreate():首次创建时调用,用于初始化资源。
    • onStartCommand():每次 startService() 调用后执行,返回值决定系统在被杀后如何重启服务(START_STICKYSTART_NOT_STICKY 等)。
    • onDestroy():在 stopSelf()stopService() 后执行,释放资源。
  • 绑定型 Service 生命周期

    onCreate()
       ↓
    onBind()
       ↓  (可多次 bind/unbind)
    onUnbind()
       ↓
    onDestroy()
    
    • onBind():客户端绑定时调用,返回 IBinder 用于客户端调用服务方法。
    • onUnbind():最后一个客户端解绑时调用,可决定是否再次允许绑定(返回 true 重写 onRebind())。

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 文件中声明

    <service
     android: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">
    
        <Button
            android:id="@+id/btn_start_service"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Start Service" />
    
        <Button
            android:id="@+id/btn_stop_service"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Stop Service" />
    
        <Button
            android:id="@+id/btn_bind_service"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Bind Service" />
    
        <Button
            android: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.Button
    
    class MainActivity : AppCompatActivity() {
        private val tag = "ServiceLifecycle"
        private var service: MyService? = null
        private var isBound = false
    
        private 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.LocalBinder
                service = localBinder.getService()
                isBound = true
                service?.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: 1
      
      onDestroy() called
      
    • 点击 Bind Service 后,再点击 Unbind Service

      onCreate() called
      onBind() called
      ServiceConnection.onServiceConnected()
      Custom method called
      
      onUnbind() 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(解耦场景)。

    • 方式 3Messenger(跨进程通信)。

  • Android 8.0 后如何替代后台服务?

    答案:

    • 使用 JobSchedulerWorkManager(兼容性更好)或前台服务。后台执行限制旨在优化电池寿命。
  • 音乐播放器为何用 Service 而不用 Thread?

    答案:

    • Service 作为独立组件,生命周期不受 Activity 影响(如退出界面仍可播放)。Thread 随 Activity 销毁可能被终止,且无法直接跨界面控制。
  • 什么是粘性服务(Sticky Service)?

    答案:

    • 通过 startService() 启动且 onStartCommand() 返回 START_STICKY 的服务,系统会在资源允许时尝试重启被杀死服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值