Service: 一、简介,分类,生命周期, 简单用法


Service 简介

借用官方话术:

  • Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。
  • 例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
  • 同时它还是 Android 四大组件之一; 另外还有 Activity, content provider, broadcast receiver

Service 分类

1、按前后台分类

分类介绍
前台服务官方介绍:    前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。
补充:    内存不足时, 一般也不会杀死前台服务. 必须要在状态栏显示一条通知, 以告知用户 服务存在.
后台服务官方介绍:    后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
补充:     多数情况下, 后台服务可以使用 JobScheduler(作业), 或者 WorkManager(jetPack库) 代替

Android 8.0 以上需要注意:

前台服务需要添加一条权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

请使用 contextstartForegroundService() 函数来启动一个 前台Service; 或者使用兼容方式

// 需要判断版本: Build.VERSION.SDK_INT >= 26
startForegroundService (intent);

// 兼容方法
ContextCompat.startForegroundService(this, intent)

2、按 远程服务, 本地服务 分类

分类介绍
本地服务(LocalService)1.跟主体应用同一进程
2.运行在主线程, 耗时任务需开子线程
3.主进程被杀死, 服务也会终止
4.单实例
远程服务(RemoteService)1.运行在独立进程
2.需要进行 IPC 通讯(AIDL, Messenger)
3.服务后台运行, 不受Activity影响

3、按是否可以通信分类

启动方式介绍
startService()不可通信
bindService()通过 ServiceConnection 接收 Service 中 onBind() 函数 返回的 IBinder 对象 进行通讯

感觉这个分类 有点鸡肋, 像是加戏 🤭

Service 生命周期

来张官方图:

在这里插入图片描述

由于存在 startService(), bindService() 两种启动方式, 它们的对应的生命周期是不同的. 因此上图是两条路线.

推荐这篇文章, 图画的不错: Service生命周期 完全解析

总结:

  • 可以看到, startService() 不会走 onBind() 方法, 也就没有 IBinder 对象, 这种Service不可通信(不算用注册广播等其他方式)
  • Service 只有一个, 多次startService() 不会重复执行 onCreate(), 但每次都会执行 onStartCommand(); 所以请把一次性的工作放在 onCreate() 中;
  • 如果 先 startService(), 随后 bindService(). 则需要先 解绑(unBindService), 然后再 停止(stopService)

Service 使用:

1.简单使用:

首先自定义类, 继承 Service. 重写需要用到的方法;

class MyService : Service() {
    val TAG = "MyService"
    override fun onCreate() {...}
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {...}
    override fun onBind(intent: Intent?): IBinder? = null
    override fun onUnbind(intent: Intent?): Boolean {...}
    override fun onDestroy() {...}
}

其次, 配置 清单文件

<!-- 前台Service 的权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> 
<service android:name=".seervice.MyService"/>

最后在 Activity 中:

val serviceIntent = Intent(this, MyService::class.java)
// 启动服务
startService(serviceIntent)
// 停止服务
stopService(serviceIntent)

2.启动一个前台服务

我们用 startForegroundService 启动服务. 不要忘记权限;

val serviceIntent = Intent(this, MyService::class.java)
ContextCompat.startForegroundService(this, serviceIntent)

当然也不能忘了通知, 不然几秒后会 ANR;
onStartCommand() 时显示通知, onDestroy() 时移除通知

class MyService : Service() {
    val TAG = "MyService"

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand")
        // 通知栏显示通知
        showNotification()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onUnbind(intent: Intent?): Boolean {
        Log.d(TAG, "onUnbind")
        return super.onUnbind(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy")
        // 移除通知
        stopForeground(true)
    }

    private fun showNotification(){
        val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val name = "我的应用"
        val channelId = "my_channel_01"
        val resultIntent = Intent(this, TestServiceActivity::class.java)
        val resultPendingIntent = PendingIntent.getActivity(this,0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT)

        val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val mChannel = NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_LOW)
            mNotificationManager.createNotificationChannel(mChannel)
            Notification.Builder(this, channelId)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("我是标题")
                .setContentText("我是内容")
                .setContentIntent(resultPendingIntent)
                .build()
        }else{
            NotificationCompat.Builder(this, channelId)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("我是标题")
                .setContentText("我是内容")
                .setContentIntent(resultPendingIntent)
                .setDefaults(Notification.DEFAULT_ALL)
                .build()
        }

        startForeground(1, notification)
    }
}

3.绑定服务后通信

首先我们改造 Service: 自定义一个 Binder 子类, 在 onBind() 函数中返回;

class MyService : Service() {
    private var mData: String? = null

    override fun onBind(intent: Intent?): IBinder = MyBinder()

    inner class MyBinder : Binder() {
        fun setData(data: String): Boolean {
            mData = data
            return mData == "设置数据"
        }
    }
}

然后 定义一个 ServiceConnection 子类;

class TestServiceActivity : ...{
	private var mBindService: MyService.MyBinder? = null
    private var mConn: MyConnection? = null
    ...
    // 点击事件
	fun onClick(v: View?){
        when(v?.id){
            R.id.tv_two -> {	// 绑定
                mConn = MyConnection()
                bindService(Intent(this, MyService::class.java), mConn!!, Context.BIND_AUTO_CREATE)
            }
            R.id.tv_three -> mConn?.let { unbindService(it) }	// 解绑
            R.id.tv_four -> { val success = mBindService?.setData("设置数据") }	// 通信
        }
    }
	...
    inner class MyConnection : ServiceConnection {
       override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
           mBindService = service as MyService.MyBinder
       }
       
       override fun onServiceDisconnected(name: ComponentName?) {}
   }
}


总结

没啥总结

补充:
IntentService? 异步任务的后台Service, 源码实现是通过 HandlerThread+Handler;
已经过时了. 如今官方建议使用 JetPack 组件中的 WorkManager 或者 JobIntentService 类代替它。


上一篇: Activity: 三、Android Result API
下一篇: Service: 二、简单的音乐播放器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值