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

  • PendingIntent会被调配给Notification,作为点击告诉后的跳转动作
  • 应用NotificationManager先创立了一个NotificationChannel
  • 用Notification.Builder配置并创立一个Notification,例如配置题目,内容文字,图标等
  • 启动前台服务,调用startForeground(1, 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

后盾服务

后盾服务执行用户不会间接留神到的操作。例如,如果利用应用某个服务来压缩其存储空间,则此服务通常是后盾服务。

  • 文中的服务/Service 指的是后盾服务。
  • 示例应用Kotlin实现。

新建服务

咱们新建一个 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()}")    }}
  • 在各个生命周期办法中咱们打上log,便于后续察看
  • log 中打出服务对象的详细信息,线程信息等等
  • onBind 办法中咱们返回null,表明这个服务不能用bindService的形式启动

在 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>

留神

  • name 是咱们的服务的类名。它是惟一必须的属性
  • enabled 为 true,表明零碎能够示例化这个服务。默认值为 true
  • application 也有本人的 enabled 属性,默认值为 true。该属性实用于所有利用组件,包含服务
  • exported 这里设置为 false,表明不给其余过程(app)应用,仅实用于咱们这个 app
    这样咱们的服务就筹备结束了。

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怎么实现通信

办法一:

  • 增加一个继承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 在另 外的过程中执行

<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" ></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的优先级?

  • AndroidManifest.xml文件中对于intent-filter能够通过android:priority = “1000”这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时实用于播送。
  • 在 onStartCommand 外面调用 startForeground()办法把Service晋升为前台过程级别,而后再onDestroy外面要记得调用stopForeground ()办法。
  • 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() 办法、创立一个无参构造函数,其代码如下:
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"); }}
  1. 增加 IntentService 组件申明,在 AndroidManifest.xml 文件中申明一个 Service 组件,其代码如下:

    <service android:name=".service.SccIntentService"/>
  2. 启动 SccIntentService

    startService(new Intent(ServiceActivity.this, SccIntentService.class));</pre>
  3. 运行后果

    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入门教程视频参考