android 8.0 对应的 sdk 版本 26
1. 通知栏
Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。
针对 8.0 的应用,创建通知前需要创建渠道,创建通知时需要传入 channelId,否则通知将不会显示。示例代码如下:
// 创建通知渠道
private void initNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = mContext.getString(R.string.app_name);
NotificationChannel channel = new NotificationChannel(mChannelId, name, NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
}
}
// 创建通知传入channelId
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationBarManager.getInstance().getChannelId());
2. 后台执行限制
如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
我们无法得知系统如何判断是否允许应用创建后台服务,所以我们目前只能简单 try-catch startService(),保证应用不会 crash,示例代码:
Intent intent = new Intent(getApplicationContext(), InitializeService.class);
intent.setAction(InitializeService.INITIALIZE_ACTION);
intent.putExtra(InitializeService.EXTRA_APP_INITIALIZE, appInitialize);
ServiceUtils.safeStartService(mApplication, intent);
public static void safeStartService(Context context, Intent intent) {
try {
context.startService(intent);
} catch (Throwable th) {
DebugLog.i("service", "start service: " + intent.getComponent() + "error: " + th);
ExceptionUtils.printExceptionTrace(th);
}
}
或者:
系统不允许后台应用创建后台服务, Android 8.0 引入了一种全新的方法,即 Context.startForegroundService(),以在前台启动新服务.
将原来 startService方式启动服务修改为 startForegroundService启动服务
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(checkIntent);
} else {
context.startService(checkIntent);
}
程序有通知的情况下:
在系统创建服务后,应用有五秒的时间来调用该服务的startForeground()方法以显示新服务的用户可见通知(如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR),在服务的onCreate()方法中调用startForeground()即可。
@Override
public void onCreate() {
super.onCreate();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
createNotificationChannel();
Notification notification = new Notification.Builder(getApplicationContext(), channelID).build();
startForeground(1, notification);
}
}
3. 允许安装未知来源应用
针对 8.0 的应用需要在 AndroidManifest.xml 中声明 REQUEST_INSTALL_PACKAGES 权限,否则将无法进行应用内升级。
4. 主题的 Activity 设置屏幕方向
将会抛出以下异常:
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation大概意思是:只有不透明的全屏Activity可以自主设置界面方向即使满足上述条件,该异常也并非一定会出现,为什么这么说,看下面两种表现:
- targetSdk=26,满足上述条件,API 26 手机没问题,API 27 手机没问题
- targetSdk=27,满足上述条件,API 26 手机Crash,API 27 手机没问题
有点摸不清 Google 的套路了……
可知,targetSdk=26 时,API 26 和 27 都没有问题,所以这个坑暂时放在适配 API 27 时再填吧。
5. 桌面图标适配
针对 8.0 的应用如果不适配桌面图标,则应用图标在 Launcher 中将会被添加白色背景:
适配方法:一起来学习Android 8.0系统的应用图标适配吧
https://mp.weixin.qq.com/s/WxgHJ1stBjokPi6lTUd1Mg
适配后的效果:
6. 隐式广播
由于 Android 8.0 引入了新的广播接收器限制,因此您应该移除所有为隐式广播 Intent 注册的广播接收器。将它们留在原位并不会在构建时或运行时令应用失效,但当应用运行在 Android 8.0 上时它们不起任何作用。
显式广播 Intent(只有您的应用可以响应的 Intent)在 Android 8.0 上仍以相同方式工作。
这个新增限制有一些例外情况。如需查看在以 Android 8.0 为目标平台的应用中仍然有效的隐式广播的列表,请参阅隐式广播例外。
developer.android.com/about/versi…
我对隐式广播的理解:
未指定广播接收器类名,通过 Action 发送。如有不妥,还请指教。
需要检查应用静态注册的隐式广播,需要改为动态注册。
7. 网络连接和 HTTP(S) 连接
Android 8.0 对网络连接和 HTTP(S) 连接行为做出了以下变更:
无正文的 OPTIONS 请求具有 Content-Length: 0 标头。之前,这些请求没有 Content-Length 标头。
HttpURLConnection 在包含斜线的主机或颁发机构名称后面附加一条斜线,使包含空路径的网址规范化。例如,它将 example.com 转化为 example.com/。
通过 ProxySelector.setDefault() 设置的自定义代理选择器仅针对所请求的网址(架构、主机和端口)。因此,仅可根据这些值选择代理。传递至自定义代理选择器的网址不包含所请求的网址的路径、查询参数或片段。
URI 不能包含空白标签。 之前,平台支持一种权宜方法,即允许主机名称中包含空白标签,但这是对 URI 的非法使用。此权宜方法只是为了确保与旧版 libcore 兼容。开发者如果对 API 使用不当,将会看到一条 ADB 消息:“URI example..com 的主机名包含空白标签。此格式不正确,将不被未来的 Android 版本所接受。”Android 8.0 废除了此权宜方法;系统对格式错误的 URI 会返回 null。
Android 8.0 在实现 HttpsURLConnection 时不会执行不安全的 TLS/SSL 协议版本回退。
对隧道 HTTP(S) 连接处理进行了如下变更: 在通过连接建立隧道 HTTP(S) 连接时,系统会在 Host 行中正确放置端口号 (:443) 并将此信息发送至中间服务器。之前,端口号仅出现在 CONNECT 行中。 系统不再将隧道连接请求中的 user-agent 和 proxy-authorization 标头发送至代理服务器。 在建立隧道时,系统不再将隧道 Http(s)URLConnection 中的 proxy-authorization 标头发送至代理。相反,由系统生成 proxy-authorization 标头,在代理响应初始请求发送 HTTP 407 后将其发送至此代理。
同样地,系统不再将 user-agent 标头由隧道连接请求复制到建立隧道的代理请求。相反,库为此请求生成 user-agent 标头。
- 如果之前执行的 connect() 函数失败,send(java.net.DatagramPacket) 函数将会引发 SocketException。 如果存在内部错误,DatagramSocket.connect() 会引发 pendingSocketException。对于 Android 8.0 之前的版本,即使 send() 调用成功,后续的 recv() 调用也会引发 SocketException。为确保一致性,现在这两个调用均会引发 > SocketException。
- 在回退到 TCP Echo 协议之前,InetAddress.isReachable() 会尝试执行 ICMP。 对于某些屏蔽端口 7 (TCP Echo) 的主机(例如 google.com),如果它们接受 ICMP Echo 协议,现在也许能够访问它们。 对于确实无法访问的主机,此项变更意味着调用需要两倍的时间才能返回结果。
developer.android.com/about/versi…
这点应用一般无需适配
8. 视图焦点
可点击的 View 对象现在默认也可以成为焦点。如果您希望 View 对象可点击但不可成为焦点,请在包含 View 的布局 XML 文件中将 android:focusable 属性设置为 false,或者将 false 传递至应用界面逻辑中的 setFocusable()。
developer.android.com/about/versi…
这点基本无需适配
9. 权限
在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。
对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 > WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 > WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。
developer.android.com/about/versi…
考拉中的权限都是按需申请的,不需要修改。
10. Tinker
特别是在Android N之后,由于混合编译的inline策略修改,对于市面上的各种方案都不太容易解决。而Tinker热补丁方案不仅支持类、So以及资源的替换,它还是2.X-8.X(1.9.0以上支持8.X)的全平台支持。
github.com/Tencent/tin…
经测试,Tinker在8.0上功能正常。
11、悬浮窗要使用类型TYPE_APPLICATION_OVERLAY,原来的类型TYPE_SYSTEM_ALERT从Android8.0开始被舍弃了。 设置悬浮窗类型的兼容代码示例如下:
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
// 设置为TYPE_SYSTEM_ALERT类型,才能悬浮在其它页面之上
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
// 注意TYPE_SYSTEM_ALERT从Android8.0开始被舍弃了
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
} else {
// 从Android8.0开始悬浮窗要使用TYPE_APPLICATION_OVERLAY
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}
12、属性动画组合AnimatorSet增加了setCurrentPlayTime和reverse方法,从而允许倒过来播放属性动画组合。 setCurrentPlayTime和reverse方法的调用方式示例如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
animSet.setCurrentPlayTime(0); // 设置当前播放的时间点
animSet.reverse(); // 从动画尾巴开始倒播至setCurrentPlayTime设置的时间点
}
App页面从全屏模式切换到画中画模式,它的Activity生命周期也会经历销毁后重建的过程,如果开发者想保持App页面不被重建,则需给该页面的activity节点加上以下的属性描述:
android:configChanges="srceenLayout|orientation"
对于视频播放页面,Activity代码同样不在onPause方法中暂停播放视频,而应当在onStop方法中暂停播放,并在onStart方法中恢复播放视频;
进入画中画模式:
转载:https://juejin.im/post/5baa1c606fb9a05d396f16ea
https://www.cnblogs.com/lrcaoxiang/p/9266944.html
隐式广播例外
作为Android 8.0(API级别26)后台执行限制的一部分,针对API级别26或更高级别的应用程序无法再在其清单中为隐式广播注册广播接收器。但是,目前有几个广播免于这些限制。无论应用程序所针对的API级别如何,应用程序都可以继续为以下广播注册监听器。
注意:即使这些隐式广播仍然在后台工作,您应该避免为它们注册侦听器。-
ACTION_LOCKED_BOOT_COMPLETED
,ACTION_BOOT_COMPLETED
- 免除,因为这些广播仅在首次启动时发送一次,并且许多应用需要接收此广播以安排作业,警报等。
-
ACTION_USER_INITIALIZE
,"android.intent.action.USER_ADDED"
,"android.intent.action.USER_REMOVED"
- 这些广播受特权权限保护,因此大多数普通应用程序无论如何都无法接收它们。
-
"android.intent.action.TIME_SET"
,ACTION_TIMEZONE_CHANGED
,ACTION_NEXT_ALARM_CLOCK_CHANGED
- 时间应用可能需要接收这些广播,以便在更改时间,时区或警报时更新警报。
-
ACTION_LOCALE_CHANGED
- 仅在语言环境发生变化时发送,这不常见。应用可能需要在区域设置更改时更新其数据。
-
ACTION_USB_ACCESSORY_ATTACHED
,ACTION_USB_ACCESSORY_DETACHED
,ACTION_USB_DEVICE_ATTACHED
,ACTION_USB_DEVICE_DETACHED
- 如果应用程序需要了解这些与USB相关的事件,目前还没有一个很好的替代方案来注册广播。
-
ACTION_CONNECTION_STATE_CHANGED
,ACTION_CONNECTION_STATE_CHANGED
,ACTION_ACL_CONNECTED
,ACTION_ACL_DISCONNECTED
- 如果应用收到这些蓝牙事件的广播,则用户体验不太可能受到影响。
-
ACTION_CARRIER_CONFIG_CHANGED
,TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED
,"TelephonyIntents.SECRET_CODE_ACTION"
,ACTION_PHONE_STATE_CHANGED
,ACTION_PHONE_ACCOUNT_REGISTERED
,ACTION_PHONE_ACCOUNT_UNREGISTERED
- OEM电话应用可能需要接收这些广播。
-
LOGIN_ACCOUNTS_CHANGED_ACTION
- 某些应用需要了解登录帐户的更改,以便他们可以为新帐户和已更改帐户设置计划操作。
-
ACTION_ACCOUNT_REMOVED
-
删除帐户后,可以看到帐户的应用会收到此广播。如果这是应用程序需要执行的唯一帐户更改,则强烈建议应用程序使用此广播而不是弃用
LOGIN_ACCOUNTS_CHANGED_ACTION
。 -
ACTION_PACKAGE_DATA_CLEARED
- 仅在用户明确清除“设置”中的数据时发送,因此广播接收器不太可能显着影响用户体验。
-
ACTION_PACKAGE_FULLY_REMOVED
-
某些应用可能需要在删除其他包时更新其存储的数据; 对于这些应用程序,没有很好的替代方案来注册此广播。
注意:其他与包相关的广播(例如
ACTION_PACKAGE_REPLACED
)不受新限制的豁免。这些广播很常见,对豁免广告有潜在的性能影响。 -
ACTION_NEW_OUTGOING_CALL
-
响应用户拨打电话而采取措施的应用需要接收此广播。
-
ACTION_DEVICE_OWNER_CHANGED
-
这种广播不经常发送; 一些应用需要接收它,以便他们知道设备的安全状态已经改变。
-
ACTION_EVENT_REMINDER
-
由日历提供商发送,以向日历应用发布活动提醒。由于日历提供商不知道日历应用程序是什么,因此该广播必须是隐含的。
-
ACTION_MEDIA_MOUNTED
,ACTION_MEDIA_CHECKING
,ACTION_MEDIA_UNMOUNTED
,ACTION_MEDIA_EJECT
,ACTION_MEDIA_UNMOUNTABLE
,ACTION_MEDIA_REMOVED
,ACTION_MEDIA_BAD_REMOVAL
-
这些广播是由于用户与设备的物理交互(安装或删除存储卷)或作为启动初始化的一部分(因为可用卷已安装)而发送的,因此它们不常见,通常由用户控制。
-
SMS_RECEIVED_ACTION
,WAP_PUSH_RECEIVED_ACTION
-
SMS收件人应用程序依赖这些广播。