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

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

2021年10月11日 07:31--浏览 · --喜欢 · --评论
粉丝:277文章:36

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

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

前台服务

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

启动前台服务

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

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

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

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

  • PendingIntent会被分配给Notification,作为点击通知后的跳转动作

  • 使用NotificationManager先创建了一个NotificationChannel

  • 用Notification.Builder配置并创建一个Notification,例如配置标题,内容文字,图标等

  • 启动前台服务,调用方法

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

停止服务

可以用  来停止服务

这样 Service 退出,走方法。

停止前台服务

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

例如在Service中调用

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

报错信息

ANR

在Activity中调用启动服务,但是不调用。 一加5手机Android10运行log如下

Bad notification

我们在ForegroundService1的方法里加入。 如果的id传入0,则会报错


后台服务

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

  • 文中的服务/Service 指的是后台服务。

  • 示例使用Kotlin实现。

新建服务

我们新建一个 ServiceStartDemo 类继承 Service

  • 在各个生命周期方法中我们打上log,便于后续观察

  • log 中打出服务对象的详细信息,线程信息等等

  • onBind 方法中我们返回null,表明这个服务不能用bindService的方式启动

在 AndroidManifest.xml 中注册这个服务

注意:

  • name 是我们的服务的类名。它是唯一必需的属性

  • enabled 为 true,表明系统可以示例化这个服务。默认值为 true

  • Application 也有自己的 enabled 属性,默认值为 true。该属性适用于所有应用组件,包括服务

  • exported 这里设置为 false,表明不给其他进程(App)使用,仅适用于我们这个 App

这样我们的服务就准备完毕了。

startService 启动服务

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

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

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

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

Activity 与 Service 沟通

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

停止服务

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

|方法|介绍|

Activity或其他组件调用这个方法,停止目标

serviceService调用这个方法来停止自己

例如在Activity中

在Service中

一旦请求使用  或  来停止服务,服务会走方法。 系统会尽快销毁服务。

绑定服务

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

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

Service 相关面试题

1. Service是什么

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

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

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

2. 注册Service需要注意什么

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

3. Service与Activity怎么实现通信

方法一:

  • 添加一个继承Binder的内部类,并添加相应的逻辑方法

  • 重写Service的onBind方法,返回我们刚刚定义的那个内部类实例

  • 重写ServiceConnection,onServiceConnected时调用逻辑方法 绑定服务

方法二

  • 通过接口Iservice调用Service方法,使用接口调用service和直接调用其实本质都是一样的,只不过多了借口一个步骤

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

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

  • 会创建独立的 worker 线程来处理所有的 Intent 请求;

  • 会创建独立的 worker 线程来处理 onHandleIntent() 方法实现的代码,无需处理多线程问题;

  • 所有请求处理完成后,IntentService 会自动停止,无需调用 stopSelf() 方法停止 Service;

  • 为 Service的onBind() 提供默认实现,返回 null;

  • 为 Service 的 onStartCommand 提供默认实现,将请求 Intent 添加到队列中;

  • IntentService 不会阻塞UI线程,而普通 Serveice 会导致 ANR 异常

  • Intentservice 若未执行完成上一次的任务,将不会新开一个线程,是等待之前的任务完成后,再执行新的任务,等任务完成后再次调用stopSelf()

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

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

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

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


6. Service的生命周期

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

  • 非绑定模式:当第一次调用 startService 的时候执行的方法依次为 onCreate()、onStartCommand(),当 Service 关闭的时候调用 onDestory 方 法。

  • 绑定模式:第一次 bindService()的时候,执行的方法为 onCreate()、 onBind()解除绑定的时候会执行 onUnbind()、onDestory()。

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

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

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

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

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

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

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

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的优先级?

  • 文件中对于intent-filter可以通过这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时实用于广播。

  • 在 onStartCommand 里面调用 方法把Service提升为前台进程级别,然后再onDestroy里面要记得调用方法。

  • onStartCommand方法,手动返回START_STICKY。

  • 在 onDestroy 方法里发广播重启 service。 service +broadcast 方式,就是当 service 走 ondestory 的时候,发送一个自定义的广播,当收到广播的时候,重新启动 service。(第三方应用或是在setting里-应用-强制停止时,App 进程就直接被干掉了,onDestroy方法都进不来,所以无法保证会执行)

  • 监听系统广播判断 Service 状态。 通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service 是否还存活。

  • Application 加上 Persistent 属性。

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

有四种返回值:

  • START_STICKY:如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。随 后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用 onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到 service,那么参数 Intent 将为 null。

  • START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。

  • START_REDELIVER_INTENT:重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。

  • START_STICKY_COMPATIBILITY: START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。

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

  • Binder: 通过 Binder 接口的形式实现,当 Activity 绑定 Service 成功的时候 Activity 会在 ServiceConnection 的类 的 onServiceConnected()回调方法中获取到 Service 的 onBind()方法 return 过来的 Binder 的子类,然后通过对象调用方法。

  • Aidl: aidl 比较适合当客户端和服务端不在同一个应用下的场景。

  • Messenger: 它引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。一个Messeger不能同时双向发送,两个就就能双向发送了

12. Service和Thread的区别

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

13. 使用IntentService

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

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

  • Service不会专门启动一个单独的进程,Service与它所在应用位于同一个进程中。

  • Service不是一条新的线程,因此不应该在Service中直接处理耗时的任务。

IntentService正好弥补了Service的不足。

IntentService的特点:

  • IntentService会创建单独的worker线程来处理所有的Intent请求。

  • IntentService会创建单独的worker线程来处理onHandleIntent()方法实现的代码,因此开发者无须处理多线程问题。

IntentService实例

1,创建 SccIntentService.java 继承自 IntentService 类,重写 onHandleIntent() 方法、创建一个无参构造函数,其代码如下:

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

3,启动 SccIntentService

4,运行结果

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

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

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

Android入门教程视频参考


投诉或建议