前言
本文主要针对Android O的适配,文中大部分内容将来自官网,本文只是总结提取出适配需要的注意点,关于Android O的一些新Feature不会提及。总的来说,Android O基本是纯后台应用的噩梦,此类应用的适配多数要面临重构、大改的窘境,而对于前台应用适配则相对轻松。
适配要点
总体而言Android O的适配主要注意以下三点:
1、后台服务访问受限,Android O不允许后台应用创建后台服务
2、诸多隐式广播被砍,清单文件静态注册无效,只能动态注册
3、运行时权限授权机制修改,只授权申请的权限,而非整个权限组的权限
后台服务访问受限
前文提及,Android O不允许后台应用创建后台服务,所以先来看看Android系统是如何区分前台与后台应用的。
1、前、后台应用区分
系统可以区分 前台 和 后台 应用。 (用于服务限制目的的后台定义与内存管理使用的定义不同;一个应用按照内存管理的定义可能处于后台,但按照Android 8上能够启动服务的定义又处于前台。)如果满足以下任意条件,应用将被视为处于前台:
1、具有可见 Activity(不管该 Activity 已启动还是已暂停)
2、具有前台服务
3、另一个前台应用已关联到该应用(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)。 例如,如果另一个应用绑定到该应用的服务,那么该应用处于前台:
IME
壁纸服务
通知侦听器
语音或文本服务
如果以上条件均不满足,应用将被视为处于后台。
以上内容摘自官网,首次阅读你可能不太理解“一个应用按照内存管理的定义可能处于后台,但按照能够启动服务的定义又处于前台”,即此处的后台定义与后台进程定义稍有出入。
内存管理中对后台进程的定义如下:
包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop()方法。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。
Android 8上能够启动服务定义中的后台:
即不满足上文所述三点中的任何一点应用被视为后台应用,留意第三点中的“不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序”,在内存管理中并未关联ContentProvider来分类进程。
假设我们有一个后台进程,此时有个前台应用关联访问了我们ContentProvider,那么此时,我们的后台进程将不会被系统判定为后台应用,而是前台应用。这就是“一个应用按照内存管理的定义可能处于后台,但按照能够启动服务的定义又处于前台”的一个具体场景。
结论:一个后台进程,在Android O上不一定被判定为后台应用。
2、后台服务限制
在了解Android O如何区分前、后台应用后,接下来看看如果后台应用强行创建后台服务会如何。
如果针对 Android O 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。
当应用处于前台时,应用可以自由创建和运行前台服务与后台服务。 进入后台时,在一个持续数分钟的时间窗内,应用仍可以创建和使用服务。
在该时间窗结束后,应用将被视为处于 空闲 状态。 此时,系统将停止应用的后台服务,就像应用已经调用服务的Service.stopSelf()方法。
在这些情况下,后台应用将被置于一个临时白名单中并持续数分钟。 位于白名单中时,应用可以无限制地启动服务,并且其后台服务也可以运行。
处理对用户可见的任务时,应用将被置于白名单中,例如:
1、处理一条高优先级Firebase 云消息传递 (FCM)消息。
2、接收广播,例如短信/彩信消息
3、从通知执行PendingIntent
在很多情况下,您的应用都可以使用 JobScheduler作业替换后台服务。 例如,CoolPhotoApp 需要检查用户是否已经从朋友那里收到共享的照片,即使该应用未在前台运行。
使用JobScheduler需要实现一个JobService,那么问题来了,JobService也是一个Service为何它能幸免于难呢?
这就与JobService的工作机制相关,简单而言,系统进程的JobSchedulerService会绑定到我们自己实现的JobService,根据上文前台应用的判别,此时我们的应用会被判定为前台应用。
隐式广播限制
Android O 的应用无法继续在其清单中为隐式广播注册广播接收器。 隐式广播是一种不专门针对该应用的广播。 例如,ACTION_PACKAGE_REPLACED 就是一种隐式广播,因为它将发送到注册的所有侦听器,让后者知道设备上的某些软件包已被替换。
不过,ACTION_MY_PACKAGE_REPLACED 不是隐式广播,因为不管已为该广播注册侦听器的其他应用有多少,它都会只发送到软件包已被替换的应用。
应用可以继续在它们的清单中注册显式广播。
应用可以在运行时使用 Context.registerReceiver() 为任意广播(不管是隐式还是显式)注册接收器。
需要签名权限的广播不受此限制所限,因为这些广播只会发送到使用相同证书签名的应用,而不是发送到设备上的所有应用。
在许多情况下,之前注册隐式广播的应用使用 JobScheduler 作业可以获得类似的功能。
注:很多隐式广播当前均已不受此限制所限。 应用可以继续在其清单中为这些广播注册接收器,不管应用针对哪个 API 级别。 有关已豁免广播的列表,请参阅隐式广播例外。
注意:
如果目标平台targetSdk设置为26,静态注册的Receiver将收不到隐式广播,即使这个隐式广播是应用自身发出的。
如果非要通过静态注册的Receiver来接受广播,则需要为Receiver注册签名权限,发送隐式广播需要使用带权限的API,如下图所示:
运行时权限修改
在 Android O 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。
对于针对 Android O 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android O,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。
关于如何优雅的进行权限的适配,请参考:
Android8.0运行时权限策略变化和适配方案
内容变更通知
Android 8.0 更改了 ContentResolver.notifyChange()和 registerContentObserver(Uri, boolean, ContentObserver)在针对 Android 8.0 的应用中的行为方式。
现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider
。使用相关权限定义一个有效的 ContentProvider可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。
单看官方文档介绍,这条是看的云里雾里,经过实践踩坑后,总结如下:
1.registerContentObserver(Uri, boolean, ContentObserver)与ContentResolver.notifyChange(),在Android O上,若程序需要监听或者通知的provider申明了权限,而执行程序并没有相应权限,则会抛出异常,导致程序崩溃。之前的版本,若没有权限不会抛出异常,只是会注册或者通知失败。
以通话记录与联系人为例,若示例代码执行时,程序不具备如下权限:
android.permission.READ_CALL_LOG
android.permission.READ_CONTACTS
则会抛出权限异常,导致程序崩溃。
- 如果你要监听或者通知的provider不存在,同样会抛出异常,导致程序崩溃。
应用快捷键
Android 8.0 对应用快捷方式做出了以下变更:
com.android.launcher.action.INSTALL_SHORTCUT
广播不再会对您的应用有任何影响,因为它现在是私有的隐式广播。相反,您应使用 ShortcutManager类中的 requestPinShortcut函数创建应用快捷方式。
现在,ACTION_CREATE_SHORTCUTIntent 可以创建可使用 ShortcutManager
类进行管理的应用快捷方式。此 Intent 还可以创建不与 ShortcutManager
交互的旧版启动器快捷方式。在以前,此 Intent 只能创建旧版启动器快捷方式。
现在,使用 requestPinShortcut()创建的快捷方式和在处理 ACTION_CREATE_SHORTCUT
Intent 的操作组件中创建的快捷方式均已转换为功能齐全的应用快捷方式。因此,应用现在可以使用 ShortcutManager中的函数来更新这些快捷方式。
旧版快捷方式仍然保留了它们在旧版 Android 中的功能,但您必须在应用中手动将它们转换成应用快捷方式。
如需了解有关应用快捷方式变更的更多信息,请参阅固定快捷方式和微件预览功能指南。
小结:
在Android O上,之前旧的创建快捷方式的方法将失效。
在8.0上,AMS直接不会转发隐式广播:
com.android.launcher.action.INSTALL_SHORTCUT
即使将targetSdkVersion设置为26以下,创建快捷方式必须使用新的API。
7.1上旧的创建快捷方式的方法依旧有效,但是所以针对非Launcher开发者而言,只需要使用新的API来创建快捷方式即可,对于Launcher开发者,要做的工作就蛮多的了,需要适配ShortcutManger及相关UI。
PS:本文只是针对主要适配点进行阐述,除了上述三点,Android O还有一些其他小细节需要适配如:
1、Android 后台位置限制(定位APP需要考虑)
2、提醒窗口(使用了SYSTEM_ALERT_WINDOW的应用需要适配)
3、记录未捕获的异常
4、隐私性(Android ID生成机制改变,一般需要统计SDK适配)
......
具体细节请参考官方文档Android O行为变更