当前位置: 首页 > news >正文

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

    <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(解耦场景)。

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

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

    答案:

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

    答案:

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

    答案:

    • 通过 startService() 启动且 onStartCommand() 返回 START_STICKY 的服务,系统会在资源允许时尝试重启被杀死服务。
http://www.xdnf.cn/news/567469.html

相关文章:

  • 安卓蓝牙frameworks/base/core/java/android/bluetooth这个路径下文件的作用
  • Android 自定义SnackBar和下滑取消
  • 命令行参数和环境变量
  • 力扣热题100,力扣148.排序链表力扣.26找出字符串中第一个匹配项的下标力扣146.LRU缓存序列管理器
  • MySQL 8.0 OCP 1Z0-908 171-180题
  • Keepalived 配置深度解析及最佳实践
  • 回表是数据库概念,还是mysql的概念?
  • MyBatis 关联映射深度解析:_association_ 与 _collection_ 实战教程
  • k8s-ServiceAccount 配置
  • spring5-配外部文件-spEL-工厂bean-FactoryBean
  • 【疑难杂症】Mysql 无报错 修改配置文件后服务启动不起来 已解决|设置远程连接
  • Redis SETNX:分布式锁与原子性操作的核心
  • Docker run命令-p参数详解
  • Python打卡训练营day28-类的定义与方法
  • 2021-04-12 VSC++: 寻找N以内的亲密数对。(求因子和)
  • 【Node.js】Web开发框架
  • 牛客网NC15869:长方体边长和计算问题解析
  • Python中的常量和变量分别是怎么定义的?
  • 【QT】在界面A打开界面B时,界面A隐藏,界面B关闭时,界面A复现
  • chromedp -—— 基于 go 的自动化操作浏览器库
  • Redis 的 key 的过期策略是怎么实现的
  • Redis String 设计思想深度解析
  • 系统架构设计师案例分析题——数据库缓存篇
  • 解除diffusers库的prompt长度限制(SDXL版)
  • ArcGIS Pro 3.4 二次开发 - 核心主机
  • Linux yq 命令使用详解
  • 【Qt】QImage实战
  • 一文读懂迁移学习:从理论到实践
  • Git Clone 原理详解:为什么它比本地文件复制更快? -优雅草卓伊凡
  • word格式相关问题