天天看点

学徒浅析Android——Android 8.0(O)后台服务的限制和变化

      Android 最近几版的特色主要集中在省电和后台管理上,O的发布,对Service和Broadcast又近一步加强了管束。主要可概括为如下两点:

      1、后台应用不被允许创建后台服务,必须通过JobScheduler或者Context.startForegroundService()进行创建

      2、特定的隐式广播不再被允许启动,必须通过JobScheduler调用或者显式注册的方式才能启动

      google之所以限制隐式广播,是因为广播的滥用导致设备在息屏等状态下,依然无法有效的做到省电,往往一个系统广播,会唤醒多个未启动的第三方应用。因此在O中,隐式广播的限制是很严厉的。谷歌给出了允许隐式注册的广播列表https://developer.android.com/preview/features/background-broadcasts.html,为了方便懒得上墙的小伙伴们,贴出整理的广播列表:

开机广播,之所以被允许,因为每次设备启动只会发送一次
1、ACTION_LOCKED_BOOT_COMPLETED(直接启动模式下才会发出)
2、ACTION_BOOT_COMPLETED

以下三个广播有权限限制,一般应用无法接受。
3、ACTION_USER_INITIALIZE
4、android.intent.action.USER_ADDED
5、android.intent.action.USER_REMOVED

地区变更广播,这个地区指的是国家一级,一般不会发送
6、ACTION_LOCALE_CHANGED
7、
时区闹钟变更相关的广播,一般时间类APP需要
7、ACTION_TIMEZONE_CHANGED
8、ACTION_NEXT_ALARM_CLOCK_CHANGED
9、android.intent.action.TIME_SET

usb事件广播,需要设备触发usb事件才能发送
10、ACTION_USB_ACCESSORY_ATTACHED
11、ACTION_USB_ACCESSORY_DETACHED
12、ACTION_USB_DEVICE_ATTACHED
13、ACTION_USB_DEVICE_DETACHED

蓝牙事件广播,需要设备触发蓝牙事件才能发送
14、ACTION_CONNECTION_STATE_CHANGED
15、ACTION_ACL_CONNECTED
16、ACTION_ACL_DISCONNECTED

OEM相关的广播,一般是OEM通讯类应用会需要
17、ACTION_CARRIER_CONFIG_CHANGED
18、TelephoneyIntents.ACTION_*_SUBSCRIPTION_CHANGED
19、TelephoneyIntents.SECRET_CODE_ACTION

登录用户状态发生改变时,这里指的是google登录,一般我们用不到,同时这个广播也在API26中废弃了。
20、LOGIN_ACCOUNTS_CHANGED_ACTION

用户通过设置页面清除掉应用数据后才会发出
21、ACTION_PACKAGE_DATA_CLEARED

来电广播
22、ACTION_NEW_OUTGOING_CALL

设备隐私状态变更广播
23、ACTION_DEVICE_OWNER_CHANGED

日历提醒广播,是由日历提供者发送给日历程序的
24、ACTION_EVENT_REMINDER

程序卸载广播,跟程序卸载相关的广播中只有这个是被允许的。
25、ACTION_PACKAGE_FULLY_REMOVED

设备媒体相关广播,但是一般会在开机时发出或者是在物理设备交互时发出。
26、ACTION_MEDIA_MOUNTED
27、ACTION_MEDIA_UNMOUNTABLE
28、ACTION_MEDIA_CHECKING
29、ACTION_MEDIA_UNMOUNTED
30、ACTION_MEDIA_EJECT
31、ACTION_MEDIA_REMOVED
32、ACTION_MEDIA_BAD_REMOVAL

短信接收广播
33、SMS_RECEIVED_ACTION
34、WAP_PUSH_RECEIVED_ACTION
           

    可以概括的说,可以进行隐式注册的广播都是非频繁发送的,或者有着特定的触发场景的。google的目的就是杜绝一个广播唤醒多个后台应用这种现象。如果我们的应用中涉及到了列表以外的广播,就有必要进行O的适配了。

    google针对前台应用的定义,认为如果满足以下任意条件,应用将被视为处于前台:

1、具有可见 Activity(不管该 Activity 已启动还是已暂停)。

   2、具有前台服务。

   3、另一个前台应用已关联到该应用(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)。
           

  但在开发中,我们会更多的遇到后台运行的需求,当应用处于后台时,监听特定的系统广播或者设置守护Service显然不能很好的适应高版本,所以按照google的剧本走才是正道。以上两各变更点都提到了JobScheduler,JobScheduler作为API21新增类,如它的名字计划任务一样,通过设置约束条件,使系统可以在不影响用户体验的情况下安排这些作业执行。应用作业不会主动挤占系统运行时间,而是在约束范围下执行工作。Android也在不断地增加JobScheduler支持的约束条件,Oreo就新增了一个运行场景:

       1、NETWORK_TYPE_METERED 作业需要一个按流量计费的网络连接,比如大多数移动数据网络数据套餐。

新增了两个约束条件:

      1、JobInfo.isRequireStorageNotLow() 如果设备的可用存储空间非常低,作业将不会运行。

      2、JobInfo.isRequireBatteryNotLow() 如果电池电量等于或低于临界阈值,作业将不会运行;临界阈值是指设备显示 Low battery warning系统对话框的电量。

  当你需要持续的后台监听功能时,用守护Service或者监听特定的高频系统广播显然已经不合时宜了,google希望你通过JobSchedular来实现类似的功能。JobScheduler的运行实质是JobService,google对JobService的定义有这样一句话:This service executes each incoming job on a {@link android.os.Handler} running on your application's main thread.表明JobService是在主线程处理任务,也就是说当一个JobService处于onStartJob()时,应用进程实际是处于激活状态的,满足上文前台定义中的第二条:具有前台服务。而任务处理又是在系统许可范围内,可谓是皆大欢喜。同时JobService是继承自Service的,它的生命周期是:

onCreated()-->onStartJob-->onStopJob()-->jobFinished()-->onDestroy()
           

onCreated()继承自Service,google对onCreated的定义是这样的:Called by the system when the service is first created.  Do not call this method directly.即当前服务被创建时会调用一次,被创建意味着这个服务之前没有存在过或者被onDestroy了。

onStartJob()-->onStopJob()这个工作阶段取决于配置的约束条件,onStartJob()不会无限制的重复执行。如果我们不设定约束条件,那么JobService在创建成功后只会调用一次onStartJob。onDestroy()同样继承自Service的,google对onDestroy的定义有这样一句话:Called by the system to notify a Service that it is no longer used and is being removed.即当系统想要回收当前服务时,便会触发onDestroy,至于间隔多久会触发取决于系统当前操作。我们可以在onDestroy()中重新执行一次创建操作,即重新通知系统安排一次计划任务。保证我们的JobScheduler始终存在于系统任务调度队列中了,这样任务就会持续的运行下去,具体操作如下:

    (1)创建一个JobService命名为DetectionService,在onCreated()中显式注册一个BroadcastReceiver,用来接收应用自身的广播或者其他系统广播,作为唤醒JobService的辅助方式。(毕竟还有安全软件这种东西的存在)

@Override
    public void onCreate() {
        super.onCreate();
        receiver = new AuxiliaryReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("your app's broadcasts");
        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
        registerReceiver(receiver, intentFilter);
    }
           

(2)在onDestroy()中执行重新创建当前JobService操作,不设定约束条件。

@Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
        JobScheduler scheduler = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            scheduler = this.getSystemService(JobScheduler.class);
        }else{
            scheduler = (JobScheduler) this.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        }
        final JobInfo.Builder builder = new JobInfo.Builder(1024,new ComponentName(this, DetectionService.class));
        builder.setPersisted(true);
        builder.setOverrideDeadline(1000);
        scheduler.schedule(builder.build());
    }
           

(3)在onStartJob()中,你可以根据自己的需求安排具体操作。

  这种设计方式,可以实现类似Service常驻后台的效果,同时符合Android O中对后台运行的要求,但是会对省电模式有影响,如果你的APP没有这方面的顾虑话可以采取这种方式,如果有省电相关的需求,那就老老实实的只用JobScheduler启动一次。google通过JobScheduler规范后台应用启动的目的,就是为了保证设备的正常休眠和应用功耗的可控,我们在开发过程中更多的去遵守google标准,才能让用户有更好的产品体验。

继续阅读