Android四大组件之 Service

Android四大组件之 Service

概览

定义

Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件

特性

  1. 生命周期独立,可在后台单独运行。
  2. 通过绑定(bindService)操作,可与组件之间进行交互。甚至可执行进程间通信(IPC)。
  3. service在其托管进程的主线程中运行,它既创建自己的线程,也在单独的进程中运行(除非另行指定)。

类型

前台服务:前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

后台服务:后台服务执行用户不会直接注意到的操作。

官方建议:API级别26以上设备中,当应用本身未在前台运行时,系统会对运行后台服务施加限制。在诸如此类的大多数情况下,您的应用应改为使用WorkManager。

绑定服务:

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

在服务和线程之间进行选择

简单地说,服务是一种即使用户未与应用交互也可在后台运行的组件,因此,只有在需要服务时才应创建服务。

如果您必须在主线程之外执行操作,但只在用户与您的应用交互时执行此操作,则应创建新线程。例如,如果您只是想在 Activity 运行的同时播放一些音乐,则可在 onCreate() 中创建线程,在 onStart() 中启动线程运行,然后在 onStop() 中停止线程。您还可考虑使用 AsyncTaskHandlerThread,而非传统的 Thread 类。如需了解有关线程的详细信息,请参阅进程和线程文档。

请记住,如果您确实要使用服务,则默认情况下,它仍会在应用的主线程中运行,因此,如果服务执行的是密集型或阻止性操作,则您仍应在服务内创建新线程。

官网还提到了一个叫IntentService的类,继承自service,默认开辟的是一个工作线程,将服务的代码放到工作线程上去运行。 由于目前我们已经有了很多异步的处理方式,所以很多情况下已经不会去用IntentService了。

生命周期

Service的操作与生命周期的关系如下图,可以清晰的看到什么操作对应什么Service生命周期:

官网提供的生命周期图在下面。

如下图所示,左边为前台/后台服务的生命周期,右边为绑定服务的生命周期。

image.png

在绑定服务的操作中,服务(Service)将会与组件(Activity)组合形成 服务器/客户端(C/S) 模式,Service作为服务器端可以与多个客户端(activity)进行绑定,若仅在绑定模式下,当最后一个客户端(activity)解绑后,service自动销毁。

为什么叫“仅在绑定模式”下。因为这绑定模式还可以和其他两种模式组合,先start,然后bind,如下图。这样就算最后一个客户端(activity)解绑后,Service也不会销毁,而是长期存在了。直到你调用stopService()stopSelf()为止。

两种模式组合

代码实现

xml配置

android:exported=" ":其他应用的组件是否能调用服务或与之交互 ,“true”表示可以,“false”表示不可以。当该值为“false”时,只有同一个应用或具有相同用户 ID 的应用的组件可以启动服务或绑定到服务。;

默认值取决于服务是否包含 Intent 过滤器。没有任何过滤器意味着服务只能通过指定其确切的类名称进行调用。这意味着服务专供应用内部使用(因为其他应用不知晓其类名称)。因此,在这种情况下,默认值为“false”。另一方面,至少存在一个过滤器意味着服务专供外部使用,因此默认值为“true”。

android:enabled=" ":系统是否可实例化服务,“true”表示可以,“false”表示不可以。默认值为“true”。

属性都为“true”(因为它们都默认使用该值)时,系统才能启用服务。任何一项为“false”都会造成服务停用,从而使系统无法将其实例化。

后台服务

最基础的Service,直接新建文件继承Service类,后在AndroidManiFest.xml中注册即可。

Intent(this,MyService::java.class).also{ startService(it) }即可调用。

绑定服务

  • 在基础的后台Service中,重写onBInd()方法。返回一个继承Ibinder的对象。
  • 我们自定义一个类继承BInder即可。里面可以存放一些数据,甚至是把当前service存放进去。
inner class MyBinder :Binder(){
        val service = this@MyService
}
  • onBind中返回这个自定义的Binder类。里面也可以进行一些线程相关操作,因为通过生命周期可知,绑定服务必会运行这个方法。
    我这里定义了个变量循环+1.
    var numLiveData = MutableLiveData(0)
    override fun onBind(intent: Intent): IBinder {
        super.onBind(intent)
        lifecycleScope.launch {
            while (true){
                delay(1000)
                numLiveData.value = numLiveData.value?.plus(1)
            }
        }
        return MyBinder()
    }
  • Activity内绑定,需要定义一个ServiceConnection对象。
val connection = object :ServiceConnection{
  override fun onServiceConnected(name: ComponentName, service: IBinder) {
        //这里拿到的service就是上一步传来的MyBinder
        (service as MyService.MyBinder)
            //      .service.numLiveData就是上一步里面循环+1的那个livedata了
   }

   override fun onServiceDisconnected(name: ComponentName) {

    }
}
//BIND_AUTO_CREATE 创建service的几种模式
bindService(intent,connection, BIND_AUTO_CREATE)

前台服务

特征

①.用户可以感知到它的存在,状态栏中会有通知;

②.不太容易会系统销毁。(如果是后台服务,从“最近使用的应用”中划出去,则服务亦将被销毁;若是前台服务则不会)

代码

  • 在service中调用startForeground()即变成了前台service。
    startForeground(id,notification)传入两个参数,id为正整数(不能为0),notification为通知。
val pendingIntent: PendingIntent =
        Intent(this, ExampleActivity::class.java).let { notificationIntent ->
            PendingIntent.getActivity(this, 0, notificationIntent, 0)
        }

//CHANNEL_ID为自定义的String值
//如果在Android8.0及以上,这个值还需要拿去做一些处理 applyNotification()
//applyNotification()代码在最后
val notification: Notification = Notification.Builder(this, CHANNEL_ID)
        .setContentTitle(getText(R.string.notification_title))
        .setContentText(getText(R.string.notification_message))
        .setSmallIcon(R.drawable.icon)
        .setContentIntent(pendingIntent)
        .setTicker(getText(R.string.ticker_text))
        .build()

// Notification ID cannot be 0.
startForeground(ONGOING_NOTIFICATION_ID, notification)
  • 既然是前台进程,得要权限

  • 最后如果Android版本大于8.0,申请通知的时候还有些额外设置。
//Android8.0以上 Google把通知的权限管理还给了用户,这里设置的值会显示在权限管理的界面
 fun applyNotification(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Create the NotificationChannel
            val name = "行行好,申请下权限呗"
            val descriptionText = "申请权限的相关描述"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
           //CHANNEL_ID在这里
            val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
            mChannel.description = descriptionText
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(mChannel)
        }
 }

参考$致谢

b站大佬longway777的教学视频
Google官方文档

你可能感兴趣的:(Android四大组件之 Service)