学习 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.mzph.cn/web/80787.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

投标环节:如何科学、合理地介绍 Elasticsearch 国产化替代方案——Easysearch?

一、Easysearch 定义 Easysearch 是由极限科技&#xff08;INFINI Labs&#xff09;自主研发的分布式搜索型数据库&#xff0c;作为 Elasticsearch 的国产化替代方案&#xff0c;基于 Elasticsearch 7.10.2 开源版本深度优化[1]。 插一句&#xff1a;Elasticsearch 7.10.2 是里…

NVC++ 介绍与使用指南

文章目录 NVC 介绍与使用指南NVC 简介安装 NVC基本使用编译纯 C 程序编译 CUDA C 程序 关键编译选项示例代码使用标准并行算法 (STDPAR)混合 CUDA 和 C 优势与限制优势限制 调试与优化 NVC 介绍与使用指南 NVC 是 NVIDIA 提供的基于 LLVM 的 C 编译器&#xff0c;专为 GPU 加速…

Veo 3 可以生成视频,并附带配乐

谷歌最新的视频生成 AI 模型 Veo 3 可以创建与其生成的剪辑相配的音频。 周二&#xff0c;在谷歌 I/O 2025 开发者大会上&#xff0c;谷歌发布了 Veo 3。该公司声称&#xff0c;这款产品可以生成音效、背景噪音&#xff0c;甚至对话&#xff0c;为其制作的视频增添配乐。谷歌表…

Android本地语音识别引擎深度对比与集成指南:Vosk vs SherpaOnnx

技术选型对比矩阵 对比维度VoskSherpaOnnx核心架构基于Kaldi二次开发ONNX Runtime + K2新一代架构模型格式专用格式(需专用工具转换)ONNX标准格式(跨框架通用)中文识别精度89.2% (TDNN模型)92.7% (Zipformer流式模型)内存占用60-150MB30-80MB迟表现320-500ms180-300ms多线程…

十四、Hive 视图 Lateral View

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月20日 专栏&#xff1a;Hive教程 在Hive中&#xff0c;我们经常需要以不同于原始表结构的方式查看或处理数据。为了简化复杂查询、提供数据抽象&#xff0c;以及处理复杂数据类型&#xff08;如数组或Map&#xff09;&#…

微软开源GraphRAG的使用教程-使用自定义数据测试GraphRAG

微软在今年4月份的时候提出了GraphRAG的概念,然后在上周开源了GraphRAG,Github链接见https://github.com/microsoft/graphrag,截止当前,已有6900+Star。 安装教程 官方推荐使用Python3.10-3.12版本,我使用Python3.10版本安装时,在初始化项目过程中会报错,切换到Python3.…

XXX企业云桌面系统建设技术方案书——基于超融合架构的安全高效云办公平台设计与实施

目录 1. 项目背景与目标1.1 背景分析1.2 建设目标2. 需求分析2.1 功能需求用户规模与场景终端兼容性2.2 非功能需求3. 系统架构设计3.1 总体架构图流程图说明3.2 技术选型对比3.3 网络设计带宽规划公式4. 详细实施方案4.1 分阶段部署计划4.2 桌面模板配置4.3 测试方案性能测试工…

数据直观分析与可视化

数据直观分析与可视化 一、数据的直观分析核心价值 数据的直观分析旨在通过视觉化的方式&#xff0c;帮助人们更直观、更快速地理解数据的特征和模式&#xff0c;从而发现趋势、异常值、分布情况以及变量之间的关系&#xff0c;为决策提供支持。 数据可视化与信息图形、信息可…

Neo4j数据库

Neo4j 是一款专门用来处理复杂关系的数据库。我们可以简单地将它理解为一个“用图结构来管理数据的工具”。与我们常见的&#xff0c;像 Excel 那样用表格&#xff08;行和列&#xff09;来存储数据的传统数据库不同&#xff0c;Neo4j 采用了一种更接近人类思维对现实世界理解的…

Java异常处理全解析:从基础到自定义

目录 &#x1f680;前言&#x1f914;异常的定义与分类&#x1f4af;运行时异常&#x1f4af;编译时异常&#x1f4af;异常的基本处理 &#x1f31f;异常的作用&#x1f427;自定义异常&#x1f4af;自定义运行时异常&#x1f4af;自定义编译时异常 ✍️异常的处理方案&#x1…

Redisson分布式集合原理及应用

Redisson是一个用于Redis的Java客户端&#xff0c;它简化了复杂的数据结构和分布式服务的使用。 适用场景对比 数据结构适用场景优点RList消息队列、任务队列、历史记录分布式共享、阻塞操作、分页查询RMap缓存、配置中心、键值关联数据支持键值对、分布式事务、TTLRSet去重集…

打破次元壁,VR 气象站开启气象学习新姿势​

在教育领域&#xff0c;VR 气象站同样发挥着巨大的作用&#xff0c;为气象教学带来了全新的模式&#xff0c;打破了传统教学的次元壁&#xff0c;让学生们以全新的姿势学习气象知识。​ 在传统的气象教学中&#xff0c;学生们主要通过课本、图片和老师的讲解来学习气象知识。这…

k8s面试题-ingress

场景&#xff1a;我通过deployment更新pod&#xff0c;ingress是怎么把新的请求流量发送到我新的pod的&#xff1f;是怎么监控到我更新的pod的&#xff1f; 在 Kubernetes 中&#xff0c;Ingress 是一种 API 对象&#xff0c;用于管理外部访问到集群内服务的 HTTP 和 HTTPS 路…

RHCE 练习三:架设一台 NFS 服务器

一、题目要求 1、开放 /nfs/shared 目录&#xff0c;供所有用户查询资料 2、开放 /nfs/upload 目录&#xff0c;为 192.168.xxx.0/24 网段主机可以上传目录&#xff0c;并将所有用户及所属的组映射为 nfs-upload,其 UID 和 GID 均为 210 3.将 /home/tom 目录仅共享给 192.16…

【动态导通电阻】GaN HEMT动态导通电阻的精确测量

2023 年 7 月,瑞士洛桑联邦理工学院的 Hongkeng Zhu 和 Elison Matioli 在《IEEE Transactions on Power Electronics》期刊发表了题为《Accurate Measurement of Dynamic ON-Resistance in GaN Transistors at Steady-State》的文章,基于提出的稳态测量方法,研究了氮化镓(…

AI 制作游戏美术素材流程分享(程序员方向粗糙版)

AI 制作游戏美术素材分享(程序员方向粗糙版) 视频讲解: 抖音:https://www.douyin.com/user/self?from_tab_namemain&modal_id7505691614690561295&showTabpost Bilibili: https://www.bilibili.com/video/BV1ojJGzZEve/ 写在最前面: 本方法比较粗糙,只对对美术风…

Java求职面试:互联网大厂技术栈深度解析

文章简述 在这篇文章中&#xff0c;我们将通过一个模拟的面试场景&#xff0c;带你深入了解Java求职面试中可能会遇到的技术栈问题。通过这个故事&#xff0c;你可以学习到相关技术点的具体应用场景和面试技巧。 正文 场景&#xff1a;某互联网大厂的面试现场 面试官&#…

学习日记-day11-5.20

完成目标&#xff1a; comment.java package com.zcr.pojo; import org.hibernate.annotations.GenericGenerator;import javax.persistence.*; //JPA操作表中数据&#xff0c;可以将对应的实体类映射到一张表上Entity(name "t_comment")//表示当前的实体类与哪张表…

机器学习第十九讲:交叉验证 → 用五次模拟考试验证真实水平

机器学习第十九讲&#xff1a;交叉验证 → 用五次模拟考试验证真实水平 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;超详细手把手指南 交叉验证…

Linux面试题集合(6)

创建多级目录或者同级目录 mkdir -p 文件名/文件名/文件名 mkdir -p 文件名 文件名 文件名 Linux创建一个文件 touch 文件名 DOS命令创建文件 echo 内容>文件名&#xff08;创建一个有内容的文件&#xff09; echo >文件名&#xff08;创建一个没有内容的文件&#xff09…