美文网首页
Android四大组件之 Service

Android四大组件之 Service

作者: 慕尼黑凌晨四点 | 来源:发表于2020-10-09 17:27 被阅读0次

    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”。

    <application><service> 属性都为“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)
    
    • 既然是前台进程,得要权限
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    
    • 最后如果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

          本文链接:https://www.haomeiwen.com/subject/tualpktx.html