文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android入门教程 | 四大组件之Service(前台服务,后台服务)

2023-12-22 22:50

关注

在这里插入图片描述

Service是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。

此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。

前台服务

台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。 即使用户停止与应用的交互,前台服务仍会继续运行。

启动前台服务

前台服务可以给用户提供界面上的操作。 每个前台服务都必须要在通知栏显示一个通知(notification)。用户可以感知到app的前台服务正在运行。 这个通知(notification)默认是不能移除的。服务停止后,通知会被系统移除。 当用户不需要直接操作app,app需要给用户一个状态显示的时候,可以用前台服务。

在 activity 中启动服务,调用startForegroundService(Intent)方法。

startForegroundService(Intent(applicationContext, ForegroundService1::class.java))

然后在 service 中,需要对应地使用startForeground方法。

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {        Log.d(TAG, "onStartCommand flags:$flags, startId:$startId [$this] ${Thread.currentThread()}")        val pendingIntent: PendingIntent =                Intent(this, ForegroundDemoAct::class.java).let { notificationIntent ->                    PendingIntent.getActivity(this, 0, notificationIntent, 0)                }        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {            val chanId = "f-channel"            val chan = NotificationChannel(chanId, "前台服务channel",                    NotificationManager.IMPORTANCE_NONE)            chan.lightColor = Color.BLUE            chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE            val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager            service.createNotificationChannel(chan)            Log.d(TAG, "服务调用startForeground")            val notification: Notification =                    Notification.Builder(applicationContext, chanId).setContentTitle("RustFisher前台服务").setContentText("https://an.rustfisher.com").setSmallIcon(R.drawable.f_zan_1).setContentIntent(pendingIntent).build()            startForeground(1, notification)        } else {            Log.d(TAG, "${Build.VERSION.SDK_INT} < O(API 26) ")        }        return super.onStartCommand(intent, flags, startId)    }` 

我们来看 service 里的这段代码。创建了一个简单的Notification

在设备上会显示出一个通知,点击这个通知,会跳转到 ForegroundDemoAct 。这是之前用 PendingIntent 设置的。

停止服务

可以用 stopService 来停止服务

stopService(Intent(applicationContext, ForegroundService1::class.java))

这样 Service 退出,走onDestroy方法。

停止前台服务

在Service中调用stopForeground(boolean)方法,能停止前台,但是不退出整个服务。 这个boolean表示是否取消掉前台服务的通知。false表示保留通知。

例如在Service中调用

stopForeground(false)

服务变成了后台服务,并没有退出。此时对应的通知可以滑动取消掉。

报错信息

ANR

在Activity中调用startForegroundService(Intent)启动服务,但是不调用Service.startForeground()。 一加5手机Android10运行log如下

2021-08-26 23:03:25.352 25551-25551/com.rustfisher.tutorial2020 D/rustAppUseStartService: 调用 startForegroundService 主线程信息Thread[main,5,main]2021-08-26 23:03:25.368 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onCreate Thread[main,5,main] rustfisher.com2021-08-26 23:03:25.370 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onStartCommand flags:0, startId:1 [com.rustfisher.tutorial2020.service.foreground.ForegroundService1@c77d408] Thread[main,5,main]2021-08-26 23:03:35.375 1596-1720/? W/ActivityManager: Bringing down service while still waiting for start foreground: ServiceRecord{53d70f2 u0 com.rustfisher.tutorial2020/.service.foreground.ForegroundService1}2021-08-26 23:03:35.382 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onDestroy [com.rustfisher.tutorial2020.service.foreground.ForegroundService1@c77d408] Thread[main,5,main]2021-08-26 23:03:52.956 1596-1720/? E/ActivityManager: ANR in com.rustfisher.tutorial2020    PID: 25551    Reason: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{53d70f2 u0 com.rustfisher.tutorial2020/.service.foreground.ForegroundService1}` 

Bad notification

我们在ForegroundService1的方法onStartCommand里加入startForeground。 如果startForeground(0, noti)的id传入0,则会报错RemoteServiceException

29871-29871/com.rustfisher.tutorial2020 E/AndroidRuntime: FATAL EXCEPTION: main    Process: com.rustfisher.tutorial2020, PID: 29871    android.app.RemoteServiceException: Bad notification for startForeground

后台服务

后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。

新建服务

我们新建一个 ServiceStartDemo 类继承 Service

class ServiceStartDemo : Service() {    companion object {        const val TAG = "rustAppStartDemoService"    }    override fun onCreate() {        super.onCreate()        Log.d(TAG, "onCreate ${Thread.currentThread()}")    }    override fun onBind(intent: Intent): IBinder? {        Log.d(TAG, "onBind ${Thread.currentThread()}")        return null    }    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {        Log.d(TAG, "onStartCommand flags:$flags, startId:$startId [$this] ${Thread.currentThread()}")        return super.onStartCommand(intent, flags, startId)    }    override fun onDestroy() {        super.onDestroy()        Log.d(TAG, "onDestroy [$this] ${Thread.currentThread()}")    }}

在 AndroidManifest.xml 中注册这个服务

<manifest xmlns:android="http://schemas.android.com/apk/res/android" >  <!-- ... -->  <application>    <service        android:name=".service.start.ServiceStartDemo"        android:enabled="true"        android:exported="false" />            <!-- ... -->  </application></manifest>

注意

startService 启动服务

在 activity 中调用 startService 方法,启动服务。

startService(Intent(applicationContext, ServiceStartDemo::class.java))

调用方法后,ServiceStartDemo服务会启动起来。 首次启动的话,服务会走onCreate和onStartCommand方法。 初始化性质的代码,放在onCreate里。

服务已经存在的情况下,用startService方法启动服务,服务会走onStartCommand方法。 此时onStartCommand里的startId会自增。用这个数值可以看出这个Service对象被启动了多少次。

同时我们可以在Service的log里观察到,Service的生命周期函数是在主线程中执行的。 因此Service也可能会遇到ANR问题。不能把过于耗时的任务放在生命周期函数里。

Activity 与 Service 沟通

Activity 与 Service 是相互独立的组件。用startService方法启动服务并不会让 activity 持有service 的实例。 它们之间可以用广播来进行沟通。或者用 EventBus 之类的工具进行沟通。

停止服务

完成任务后,我们可以停止服务。节省系统资源。 前面是用startService方法启动的服务,后面用stopService(Intent)来停止服务。

方法介绍
stopService(Intent)Activity或其他组件调用这个方法,停止目标service
stopSelf()Service调用这个方法来停止自己

例如在Activity中

stopService(Intent(applicationContext, ServiceStartDemo::class.java))

在Service中

stopSelf()

一旦请求使用 stopSelf()stopService() 来停止服务,服务会走onDestroy()方法。 系统会尽快销毁服务。

绑定服务

当应用组件通过调用bindService()绑定到服务时,服务即处于绑定状态。

绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

Service 相关面试题

1. Service是什么

Service 是 Android 四大组件之一,它可以在后台执行长时间运行操作而没有用户界面的应用组件。

Service 的启动方式有两种:startService 启动和 bindService 启动。

注意:服务与其他应用程序对象一样,在其托管进程的主线程中运行。这意味着,如果你的服务要执行任何CPU密集型(例如 MP3 播放)或阻塞(例如网络)操作,它应该在Service中再创建一个子线程,然后在这里去处理耗时操作就没问题了。

2. 注册Service需要注意什么

Service 还是运行在主线程当中的,所以如果需要执行一些复杂的逻辑操作,最好在服务的内部手动创建子线程进行处理,否则会出现UI线程被阻塞的问题。

3. Service与Activity怎么实现通信

方法一:

方法二

4. IntentService与Service的区别(intentservice的优点)

IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题

5. Service 是否在 main thread 中执行, service 里面是否 能执行耗时的操作?

默认情况,如果没有显示的指 service 所运行的进程, Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面。

service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 )

特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行

<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" ></service>

6. Service的生命周期

Service 有绑定模式和非绑定模式,以及这两种模式的混合使用方式。不同 的使用方法生命周期方法也不同。

上面的两种生命周期是在相对单纯的模式下的情形。我们在开发的过程中还 必须注意 Service 实例只会有一个,也就是说如果当前要启动的 Service 已经存 在了那么就不会再次创建该 Service 当然也不会调用 onCreate()方法。

一个 Service 可以被多个客户进行绑定,只有所有的绑定对象都执行了

onBind() 方法后该 Service 才会销毁,不过如果有一个客户执行了 onStart() 方法,那么这个时候如果所有的 bind 客户都执行了 unBind() 该 Service 也不会 销毁。

Service 的生命周期图如下所示,帮助大家记忆。

只使用 startService 启动服务的生命周期

img

只使用BindService绑定服务的生命周期

img

同时使用 startService() 启动服务、BindService() 绑定服务的生命周期

img

7. Activity、Intent、Service 是什么关系

他们都是 Android 开发中使用频率最高的类。其中 Activity 和 Service 都是 Android 四大组件之一。他俩都是 Context 类的子类 ContextWrapper 的子类, 因此他俩可以算是兄弟关系吧。不过兄弟俩各有各自的本领, Activity 负责用户 界面的显示和交互, Service 负责后台任务的处理。Activity 和 Service 之间可 以通过 Intent 传递数据,因此可以把 Intent 看作是通信使者。

8. Service 和 Activity 在同一个线程吗?

对于同一 app 来说默认情况下是在同一个线程中的,main Thread (UI Thread)。

9. 如何提高service的优先级?

10. Service 的 onStartCommand 方法有几种返回值?各代表什么意思?

有四种返回值:

11. Activity 调用 Service 中的方法都有哪些方式?

img

12. Service和Thread的区别

Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。 Thread是程序执行的最小单元,分配 CPU 的基本单位,可以开启子线程执行耗时操作。 Service 在不同 Activity 中可以获取自身实例,可以方便的对 Service 进行操作。 Thread 在不同的 Activity 中难以获取自身实例,如果 Activity 被销毁,Thread实例就很难再获取得到。

13. 使用IntentService

IntentService 是 Scrvice 的子类,因此它不是普通的 Service,它比普通的Service 增加了额外的功能。

先看 Service 本身存在的两个问题。

IntentService正好弥补了Service的不足。

IntentService的特点:

IntentService实例

  1. 创建 SccIntentService.java 继承自 IntentService 类,重写 onHandleIntent() 方法、创建一个无参构造函数,其代码如下:
public class SccIntentService extends IntentService { public SccIntentService() { super("SccIntentService"); } @Override protected void onHandleIntent(Intent intent) { MLog.e(getClass().getName(), "onHandleWork"); for (int i = 0; i < 3; i++) { try { MLog.e(getClass().getName(), "Number:开始"+i); Thread.sleep(10000); MLog.e(getClass().getName(), "Number:结束"+i); } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void onDestroy() { super.onDestroy(); MLog.e(getClass().getName(), "onDestroy"); }}

\2. 添加 IntentService 组件声明,在 AndroidManifest.xml 文件中声明一个 Service 组件,其代码如下:

<service android:name=".service.SccIntentService"/>

\3. 启动 SccIntentService

startService(new Intent(ServiceActivity.this, SccIntentService.class));</pre>

\4. 运行结果

07-07 18:00:39.505 E/-SCC-: com.scc.demo.actvitiy.ServiceActivityonCreate07-07 18:00:39.531 E/-SCC-: com.scc.demo.actvitiy.ServiceActivityonStart07-07 18:00:39.531 E/-SCC-: com.scc.demo.actvitiy.ServiceActivityonResume07-07 18:01:12.690 E/-SCC-com.scc.demo.service.SccIntentService: onHandleWork07-07 18:01:12.690 E/-SCC-com.scc.demo.service.SccIntentService: Number:开始007-07 18:01:22.691 E/-SCC-com.scc.demo.service.SccIntentService: Number:结束007-07 18:01:22.697 E/-SCC-com.scc.demo.service.SccIntentService: Number:开始107-07 18:01:32.698 E/-SCC-com.scc.demo.service.SccIntentService: Number:结束107-07 18:01:32.698 E/-SCC-com.scc.demo.service.SccIntentService: Number:开始207-07 18:01:42.699 E/-SCC-com.scc.demo.service.SccIntentService: Number:结束207-07 18:01:42.716 E/-SCC-com.scc.demo.service.SccIntentService: onDestroy

普通 Service 直接执行 20S 的的耗时操作,会阻塞主线程,造成 ANR (程序无响应)异常。

IntentService 执行30S的耗时操作,不会阻塞主线程,更不会产生ANR。如上图开始18:01:12>18:01:42长达30S,正常运行未产生ANR。

IntentService还有个好处就是 「用完即走」。执行完onHandleIntent()方法里面的耗时操作后,自行调用onDestroy()方法,进行关闭。

更多Android零基础入门到精通的资料完整资料可以扫码免费领取!!!

【腾讯技术团队出品】Android零基础入门到精通,Android Studio安装教程+全套安卓基础教程

Android编程入门教程

Java语言基础从入门到熟悉

在这里插入图片描述

Kotlin语言基础从入门到熟悉

在这里插入图片描述

Android 技术栈从入门到熟悉

在这里插入图片描述

Android Jetpack 全家桶全面学习

在这里插入图片描述

对于新手来说可能安装Android Studio存在一定困难你可以看着以下视频,一步步的跟着学习安装运行

Android Studio 安装教程

在这里插入图片描述

有了Java阶段的学习,这一阶段建议以视频学习为主辅以图书查漏补缺。如果以图书为主,可以根据图书讲解敲代码,辅以教学视频查漏补缺。遇到问题可以去百度,入门的问题一般会有很多人遇到,并且给出比较好的解答。

需要掌握基本知识点,比如四大组件如何使用、如何创建Service、如何进行布局、简单的自定义View、动画、网络通信等常见技术。

全套零基础教程已经为你们准备好了,需要的可以添加下方二维码免费领取

全套安卓基础教程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

来源地址:https://blog.csdn.net/Android23333/article/details/132423453

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯