Android四大组件-Service

启动方式

独立启动

Context.startService(Intent service),和启动它的组件无关,有自己的生命周期,在后台一直运行直到运行完成或者别的组件关闭了它。

使用场景

  • 后台音乐播放
  • 后台下载
  • 常驻型服务

生命周期

  • onCreate()
  • onStartCommand(Intent, int, int)
  • onDestroy()

绑定启动

Context.bindService(Intent service, ServiceConnection conn,int flags),和启动它的组件绑定,可以与绑定它的组件之间长时间交互。

使用场景

  • 前台音乐播放(UI更新)
  • 数据计算,需要随组件启动和销毁,需要交互。

生命周期

  • onCreate()
  • onBind(intent: Intent): IBinder
  • onUnbind(intent: Intent?): Boolean
  • onDestroy()

特性

  • 不需要直接和用户交互,被设计用于长时间的后台操作。需要在Manifest中进行注册。

  • 不是一个子线程。和别的组件一样运行在Main Thread中,耗时操作需要另起新现成或者使用IntentService,否则会造成ANR错误。

  • 不是一个单独的进程,除非特殊声明。

  • 可以通过startForeground()方法指定前台服务,高优先级,不会被系统杀掉。

  • 启动Service有两个额外的操作模式,设置方法为onStartCommand():的返回值:

    • START_STICKY代表需要明确的启动和停止。
    • START_NOT_STICKYorSTART_REDELIVER_INTENT代表没有明确的停止,会在后台一直运行。
  • 绑定启动具体实现:

    • 需要在service中定义IBinder的子类,用于组件与Service之间通信。(其实也就是类似一个回调接口,没有明确规定,可以写任何方法和属性)
    • 在启动Service的组件中,定义ServiceConnection,用于得到IBinder实例,以及连接成功和断开连接的回调方法。
    public class MService extends Service {
    
        private Notification mNotification;
    
        private class MyBinder extends Binder {
    
            void start() {
                Log.i("TAG", "startService");
            }
        }
    
        private MyBinder mMyBinder = new MyBinder();
    
        @Override
        public void onCreate() {
            super.onCreate();
            mNotification = new Notification.Builder(this)
                    .setSmallIcon(R.mipmap.icon)
                    .build();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mMyBinder;
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            //前台服务 高优先级
            startForeground(1, mNotification);
            //返回值决定额外的操作模式
            return START_STICKY;
        }
    }
    
  • 特殊用法

    • 需要和组件之间进行通信,但是又不希望在组件停止之后就停止运行先通过Context.startService()启动service再调用Context.bindService()进行绑定实现绑定独立运行的服务。

多次启动

Service是支持多次调用启动方法的,但是在一次生命周期中,onCreate 、onBind、onUnbind、onDestroy方法只会执行一次,onStartCommand 可执行多次,并且独立启动和绑定启动是可以交叉使用的。

举个栗子:

下面统称Context.startService() 为 start,Context.bindService() 为 bind,Context.unBindService() 为 unbind,Context.stopService() 为 stop

执行顺序: start -> bind -> bind -> start -> unbind -> stop
生命周期:
onCreate() ->
onStartCommand() ->
onBind() ->
onStartCommand() ->
onUnbind() ->
onDestroy()

执行顺序: bind-> bind-> start -> start -> stop
生命周期:
onCreate() ->
onBind() ->
onStartCommand() ->
onStartCommand() ->
onDestroy()

需要注意的地方

  1. 同一个context在一次生命周期内只能调用一次bind
  2. 同一个Service可以被多个context bind,但是onbind仅会执行一次
  3. 同一个context在一次生命周期内也可以调用多次start,且onStartCommand会执行多次
  4. 通过start启动的也能通过 bind 后期绑定,但是只能通过stop结束
  5. 通过bind启动的也能通过 start 执行到 onStartCommand,在所有bind的context全部unbind后结束 并且切换为start模式,通过stop结束

版本适配

5.0(21)

为确保安全性,使用推荐使用显式Intent,如果使用隐式Intent调用BindService将会抛出异常

8.0(26)

当应用未在前台,无法通过startService创建后台服务,可考虑使用startForegroundService创建前台服务

9.0(28)

如果需要创建前台服务需要在Manifest中声明权限FOREGROUND_SERVICE,否则将抛出SecurityException

12(31)

  • 开启前台服务通知将会延迟10s显示,例外情况参考
  • 后台应用无法创建前台服务,由于8.0限制了后台服务创建,也就是无法创建任务服务,例外情况参考

后台启动服务思路

从Android 12 之后系统完全禁止了通过后台启动Service的方式,那如果确实需要有这种需求应该怎么办呢,这里基于官方的赦免说明提供以下思路:

广播启动

通过赦免的系统静态广播启动,可参考Android四大组件-BroadcastReceiver

省电白名单

通过引导用户手动将应用添加至省电白名单

    /**
     * 请求用户添加省电白名单
     */
    @SuppressLint("BatteryLife", "QueryPermissionsNeeded")
    private fun requestIgnoreBatteryOptimization() {
   		// 判断当前是否处于白名单
        if (!(getSystemService(POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(
                packageName
            )
        ) {
        	// 开启引导弹窗
            val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
            intent.data = Uri.parse("package:$packageName")
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(intent)
            }
        }
    }

值得注意的是,上述官方调用代码国内很多定(流)制(氓)ROM似乎并不能生效,因此在业务中大概率还是需要针对不同厂商定制不同的用户引导说明

服务意义

  • Android中的后台操作主要分为两种:ThreadService。而Service又分为前台服务和后台服务。既然线程也能后台执行那还要服务来干嘛,这里就涉及到Android系统的优先级了。
    通常优先级排列如下:线程 < 后台服务 < 前台服务 。因此需要后台操作具有一定稳定性时推荐使用服务,需要后台操作完全可靠时使用前台服务,对后台操作要求不高时使用线程。
  • 通常一个应用在系统层面就是一个进程,Android系统为每个进程分配的资源都是有限的,但是这不代表一个应用只允许存在单进程,在AndroidManifest.xml中,可以为四大组件配置android:process="xxx"来使该组件在新进程中运行。
    而跨进程通信IPC(Inter-Process Communication)是Service的强项,可以说才是Service存在的最重要的意义
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值