Android 8.0 Oreo 新特性:

1.自动填充功能 API

其实就是EditText自动记住用户名密码功能,需要用户登录google帐号,并允许自动记录自动填充功能。
打开服务的方式 Settings > System > Languages & input > Advanced > Input assistance > Autofill service.
并且自填充功能也需要设置一个密码,当输入框第一次获取焦点时,会弹出一个小窗口是否自填充,如果有自填充记录,会先让用户输入这个密码,然后就会自动填充对应的用户名和密码。
也可以长按编辑框弹出的小窗口里点击更多,也有自动填充选项。
直接在EditText加一个参数 android:autofillHints=”username” 或 android:autofillHints=”password”即可快速应用。
Demo

调查一下有没有简单的方式
没有简单的方式,但是设置服务只需设置一次。使用自填充服务时没有省略密码的途径。
已尝试换了一个新的包名,当作令一个新服务。设置里面服务是单选框,只能选择一个服务。这个服务相当于是系统的一套服务,可以给所有带autofillHints标签的EditText通用。
代码已验证 :不同包名的应用,只要android:autofillHints标签一直,可以通用一套历史数据。
标签不同提示的用户内容也不同。
不同标签的EditText第一次输入新账户时,会提示是否保存到自填充服务中。

2.画中画功能 API

其实国内好多应用都支持这个功能了,现在google是集成到系统层。
类似微信悬浮窗口视频功能。可以在播放视频时使用
使用方法:
先在Manifest里声明当前Activity支持画中画

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
    ...
/>

切换方式:

//切换到画中画模式,由 args 指定纵横比和其他配置设置
    Activity.enterPictureInPictureMode(PictureInPictureParams args)

在Activity切换到画中画模式时,系统会认为Activity处于暂停状态,并调用Activity的onPause()方法。
所以应该在onPause()方法中处理一下,让视频继续播放。在Activity的onPause()方法中检查画中画,并对播放做相应处理。
例如:

@Override
public void onPause() {
    // If called while in PIP mode, do not pause playback
    if (isInPictureInPictureMode()) {
        // Continue playback
        ...
    }
}

因为画中画界面很小,所以在Activity进入画中画模式时,Activity应仅显示视频播放,隐藏掉一些不必要的UI元素

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode);
    if (isInPictureInPictureMode) {
        //处理UI界面,隐藏掉控制按钮等,只留下视频播放窗口
    } else {
        //恢复UI界面,启用控制按钮视频详情等UI元素
    }
}

Demo

同一个应用里是否支持画中画
已验证,可以在同一个应用中,支持画中画,点击画中画模式后,背景界面会返回上一个Activity。
已验证,系统不支持同时开启多个小窗口,系统只显示最后一个,不论这两个小窗口是不是同一个app的。

3.通知 API

Notification Dots:有新消息时,图标右上角会有小圆圈。并且长按图标,也会显示通知详情。

设置颜色,方便凸显自己应用的通知。

Notification.Builder.setColor()

设置通知超时,如果一个通知显示过长时间用户没有处理,可以自动消失。

Notification.Builder.setTimeoutAfter()

休眠:系统新功能。在通知处横滑,有选择休眠一小时的功能。

通知清除:系统现在可区分通知是由用户清除,还是由应用移除。要查看清除通知的方式,应实现 NotificationListenerService 类的新 onNotificationRemoved() 函数。

发送的消息不仅可以通过id区分,还可以通过自定义类别区分
新的特性看在demo中查看:

调起系统设置界面

Intent i = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
    i.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
    i.putExtra(Settings.EXTRA_CHANNEL_ID, channel);//自定义的通知类型,可以不传递此参数
    startActivity(i);

Demo

如果不传参数,会不会默认一类
调起设置界面可以不传通知类型参数。
老式的用法是NotificationCompat.Builder,此用法不需要传递通知类型参数,8.0任然可以使用此方法,是默认一种类型。
新式的用法是Notification.Builder,需要传递类型参数
启动通知时,必须先声明这个参数,再使用,不能不传参数。

//创建一个通知类型:
NotificationChannel chan1 = new NotificationChannel(channelType, title, NotificationManager.IMPORTANCE_DEFAULT);
chan1.setLightColor(Color.GREEN);
chan1.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
getManager().createNotificationChannel(chan1);
//只能使用已创建的通知类型channelType,不传或者传未创建的,可以编译通过,但运行时会弹出Toast报错。
Notification.Builder builder = Notification.Builder(getApplicationContext(), channelType)
     .setContentTitle(title)
     .setContentText(body)
     .setTimeoutAfter(3000)
     .setColorized(true)
     .setColor(0xff00ff00)
     .setSmallIcon(getSmallIcon())
     .setAutoCancel(true);
NotificationManager.notify(id, builder.build());

已验证可以每次通知都创建一个新的通知类别。

新的onNotificationRemoved逻辑
添加了一个新函数onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason)
其中reason参数,可判断通知是被什么清除掉的。
参数有:
REASON_CANCEL 测试为,用户手动划掉这条通知
REASON_CANCEL_ALL 在下拉状态栏里,点的全部清除。
REASON_TIMEOUT 在设置的超时时间,超时之后,自动消失的。
REASON_GROUP_SUMMARY_CANCELED 按照整个应用的通知一起清除的。
REASON_CLICK 用户点击通知后清除的
REASON_CHANNEL_BANNED,清除原因为:用户禁止了当前类型的通知。就是本来有一条通知,搓动一下显示出设置,点击禁止通知这类消息,然后onNotificationRemoved就会返回这个reason。但是再次发送这个通知时,因为已经禁止通知,所以也就不会有reason返回。因此无法通过onNotificationRemoved监听监听通知没弹成功而被禁止掉的情况。
More
通知没弹成功,是否是能知道被禁止掉

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    manager.areNotificationsEnabled();//可以来判断当前应用的通知权限是否被禁止掉。

4.Instant Apps功能,

就是不需要下载应用即可使用的app
允许用户在Web浏览器上体验这款应用程序,而不需要下载安装。
需要把应用转换成模块,这样更小的数据包就能尽快运行(不必访问其完整应用)

Google 只推出了其中的四个:BuzzFeed、Periscope、Viki 和 Wish,这四款 Instant Apps 已经准备好交由公众测试,以收集用户反馈并改善框架。

应用程序作为 Instant Apps 运行的指南。主要内容包括:
1.删除你的应用程序中不需要的批量。
2.支持基于 URL 的导航,这是基于 Android 6.0 中引入的 App Links 实现的。导航到目标活动和活动之间是通过和这些活动相关的 URL 链接实现的。
3.必要时重构代码,模块大小必须小于 4MB。
4.使用 Android 6+ 中存在的运行环境权限。
5.如果应用程序验证用户,请使用 Smart Lock for Passwords。
6.将不支持的功能移动到加载了已安装应用程序的单独库中,但 Instant App 不适用。不支持加载 Instant Apps 的一些方法包括:后台服务、内容提供者、广播接收者和推送通知。Instant Apps 不能访问外部存储设备,特意发现并与设备上的应用程序互动,接收广播,访问 GCM,访问某些设备标识符或是更改设置。大多数此功能在 Instant Apps 中禁止使用以保护用户。
  尽管 Instant Apps 依赖于 Android 6.0 中引入的运行环境权限,但它们也可以在早期 Android 版本上运行(从 4.1+ API 16+ 开始),但请注意需要 Play Services。

5.Emoji表情换成圆头风格,并添加新表情

Demo

6.同时支持可下载字体,

应用程序不再需要捆绑定制字体,缩小的apk体积
Demo

7.自适应TextView的字体大小 API

可设置最大值最小值以及间隔值,字体大小就会随着TextView的大小而自动变大变小
参考样例:

<TextView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:autoSizeTextType="uniform"
    android:autoSizeMinTextSize="12sp"
    android:autoSizeMaxTextSize="100sp"
    android:autoSizeStepGranularity="2sp" />

8.Adaptive Icons 自适应图标 API

需要提供一个大分辨率的方形icon

为了帮助开发者更好的与设备 UI 集成,Android O 支持创建自适应图标,系统可以基于设备选择的蒙版将这些图标显示为不同形状。系统还将实现与图标的自动交互,并在启动器、快捷方式、设置、共享对话框以及概览屏幕中使用它们。
通过定义两张图层(前景与背景)你可以制定你的桌面图标外观,你必须提供没有形状和阴影的 PNG 格式图象作为图层。
在以前的 Android 版本中,图标大小定义为 48 x 48 dp。现在你必须按照以下的规范定义你的图层大小:
两张图层大小都必须为 108 x 108 dp。
图层中心 72 x 72 dp 范围为可视范围。
系统会保留四周外的 36dp 范围用于生成有趣的视觉效果(如视差和跳动)。

现在图标可以设置两种,普通的,和圆形的,
只需在application添加

<application
    android:icon="@mipmap/ic_launcher"
    android:roundIcon="@mipmap/ic_launcher_round"
/>

接下来需要 res/mipmap-anydpi/ic_launcher.xml 文件中定义图层,加入前景和背景图层。

<adaptive-icon>
    <background android:drawable="@color/ic_background"/>
    <foreground android:drawable="@mipmap/ic_foreground"/>
</adaptive-icon>

9.http(s)新的规则变更,

规定了一些更加规范的使用方法,没有新功能

1.无正文的 OPTIONS 请求具有 Content-Length: 0 标头。之前,这些请求没有 Content-Length 标头。
2.HttpURLConnection 在包含斜线的主机或颁发机构名称后面附加一条斜线,使包含空路径的网址规范化。例如,它将 http://example.com 转化为 http://example.com/
3.通过 ProxySelector.setDefault ( ) 设置的自定义代理选择器仅针对所请求的网址(架构、主机和端口)。因此,仅可根据这些值选择代理。传递至自定义代理选择器的网址不包含所请求的网址的路径、查询参数或片段。
4.URI 不能包含空白标签。
之前,平台支持一种权宜方法,即允许主机名称中包含空白标签,但这是对 URI 的非法使用。此权宜方法只是为了确保与旧版 libcore 兼容。开发者如果对 API 使用不当,将会看到一条 ADB 消息:“URI example..com 的主机名包含空白标签。此格式不正确,将不被未来的 Android 版本所接受。”Android 8.0 废除了此权宜方法;系统对格式错误的 URI 会返回 null。
5.Android 8.0 在实现 HttpsURLConnection 时不会执行不安全的 TLS/SSL 协议版本回退。
6.对隧道 HTTP(S) 连接处理进行了如下变更:
在通过连接建立隧道 HTTP(S) 连接时,系统会在 Host 行中正确放置端口号 (:443) 并将此信息发送至中间服务器。之前,端口号仅出现在 CONNECT 行中。
系统不再将隧道连接请求中的 user-agent 和 proxy-authorization 标头发送至代理服务器。
在建立隧道时,系统不再将隧道 Http(s)URLConnection 中的 proxy-authorization 标头发送至代理。相反,由系统生成 proxy-authorization 标头,在代理响应初始请求发送 HTTP 407 后将其发送至此代理。
同样地,系统不再将 user-agent 标头由隧道连接请求复制到建立隧道的代理请求。相反,库为此请求生成 user-agent 标头。
7.如果之前执行的 connect() 函数失败,send(java.net.DatagramPacket) 函数将会引发 SocketException。
如果存在内部错误,DatagramSocket.connect() 会引发 pendingSocketException。对于 Android 8.0 之前的版本,即使 send() 调用成功,后续的 recv() 调用也会引发 SocketException。为确保一致性,现在这两个调用均会引发 SocketException。
8.在回退到 TCP Echo 协议之前,InetAddress.isReachable() 会尝试执行 ICMP。
对于某些屏蔽端口 7 (TCP Echo) 的主机(例如 google.com),如果它们接受 ICMP Echo 协议,现在也许能够访问它们。
对于确实无法访问的主机,此项变更意味着调用需要两倍的时间才能返回结果。

10.添加了xml布局文件两个属性:

layout_marginVertical:纵向外边距,就是上下同时边距一个值,设置了这个参数后,layout_marginTop和layout_marginBottom就不起作用了。
layout_marginHorizontal:横向外边距,就是左右同时边距一个值,
paddingVertical,纵向内边距,就是上下同时边距一个值,
paddingHorizontal,横向内边距,就是左右同时边距一个值

已实际代码测试设置了layout_marginVertical参数后,layout_marginTop和layout_marginBottom就不起作用了。

11.findViewById() 函数的全部实例均返回 <T extends View> T,而不是 View。

方便写代码,不需要强制类型转换,例如:
Button button = findViewById(R.id.btnA);

12.添加的小功能

添加指纹手势功能API
就相当于ZUK的U-touch键,在指纹传感器上下左右滑动。可以app自定义手势事件。

固定快捷方式和小部件API
可以把一些快捷功能添加到长按应用图标弹出的菜单里。

XML 中的字体API
就是可以直接在xml设置某个TextView的字体格式

应用类别
Android 8.0 允许每个应用声明其所属的类别。这些类别用于将应用呈现给用户的用途或功能类似的应用归类在一起,例如按流量消耗、电池消耗和存储消耗将应用归类。在 <application>清单标记中设置 android:appCategory 属性,定义应用的类别。

类别是否是自定义的
android:appCategory 属性参数不是自定义的,可选参数如下:
audio,game,image,maps,news,productivity,social,video

13.新添加的权限

READ_PHONE_NUMBERS 权限允许您的应用读取设备中存储的电话号码。

14蓝牙API

Android 8.0 对 ScanRecord.getBytes() 函数检索的数据长度做出以下变更:
getBytes()函数对于所接收的字节数不作任何假定。
因此,应用不应受所返回的任何最小或最大字节数的影响。相反,应用应当计算所返回数组的长度。
兼容蓝牙 5 的设备返回的数据长度可能会超出之前最大约 60 个字节的限制。
如果远程设备未提供扫描响应,则也可能返回少于 60 个字节的数据。

15悬浮在其他应用之上的提醒窗口。

运行在8.0的机器上,需要注意;
使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。
使用 TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:
应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
系统可以移动使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口或调整其大小,以改善屏幕显示效果。
通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。

调查一下 TYPE_APPLICATION_OVERLAY 的新窗口类型,试验是否影响自定义锁屏
将锁屏界面的类型由TYPE_PHONE设置为TYPE_APPLICATION_OVERLAY 后,也可以正常锁屏,锁应用。但是状态栏会有一条通知,提示自定义锁屏正显示在其他应用之上,可以提示用户是否关闭这个权限。

已验证:如果应用还是使用的TYPE_PHONE等旧的参数,系统自动当作TYPE_APPLICATION_OVERLAY类型来处理。
当在其他应用上层显示锁屏窗口时,状态栏里也会有一条通知,提示用户是否关闭自定义锁屏在其他应用上层显示的权限。
用户关闭这个权限后,自定义锁屏就直接消失了,解决办法,将Android系统的通知某个应用正在其他应用上层显示的这条通知关闭掉,就不会显示这条通知了。
可以在第一次设置自定义锁屏时,引导用户这么设置。
再次显示此通知方法:设置-应用和通知-通知-通知-更多-显示系统进程-Android系统-打开显示在其他应用上层的通知。
代码打开方式:

Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
    intent.putExtra(Settings.EXTRA_APP_PACKAGE, "android");//因为“Android系统”进程的包名就叫 android
    startActivity(intent);

TYPE_TOAST也属于Android8.0 的过期参数,也会自动被当作TYPE_APPLICATION_OVERLAY类型处理。

16.安全性方面的更改

1.如果您的应用的网络安全性配置选择退出明文通信的支持,那么您的应用的 WebView 对象无法通过 HTTP 访问网站。每个 WebView 对象必须转而使用 HTTPS。
例如配置了res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">secure.example.com</domain>
    </domain-config>
</network-security-config>

2.Android8.0不再支持 SSLv3
3.在与未正确实现 TLS 协议版本协商的服务器建立 HTTPS 连接时,HttpsURLConnection 不再尝试回退到之前的 TLS 协议版本并重试的权宜方法
4.查询 net.hostname 系统属性返回的结果为空。

5.内容变更通知
Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在针对 Android 8.0 的应用中的行为方式。
现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider。使用相关权限定义一个有效的 ContentProvider 可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。
ContentResolver添加了一些静态参数,添加了refresh()函数。在api里搜26即可
详见API
ContentProvider也添加了refresh()函数
API

查一查ContentProvider的更新
ContentResolver.notifyChange()和registerContentObserver函数在Android 8.0以后,所有的内容通知必须基于一个有效的ContentProvider

6.新的帐号访问和 Discovery API
Android 8.0 对应用访问用户帐号的方式引入多项改进。对于由身份验证器管理的帐号,身份验证器在决定对应用隐藏帐号还是显示帐号时可以使用自己的策略。Android 系统跟踪可以访问特定帐号的应用。
在以前的 Android 版本中,想要跟踪用户帐号列表的应用必须获取有关所有帐号的更新,包括具有不相关类型的帐号。Android 8.0 添加了 addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]) 函数,其允许应用指定应接收帐号变更的帐号类型列表。
AccountManager 提供六个新函数以帮助身份验证器管理哪些应用可以查看某个帐号:
setAccountVisibility(android.accounts.Account, java.lang.String, int):针对特定用户帐号和软件包组合设置可见性级别。
getAccountVisibility(android.accounts.Account, java.lang.String):获取特定用户帐号和软件包组合的可见性级别。
getAccountsAndVisibilityForPackage(java.lang.String, java.lang.String):允许身份验证器获取帐号和给定软件包的可见性级别。
getPackagesAndVisibilityForAccount(android.accounts.Account):允许身份验证器获取存储的给定帐号的可见性值。
addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, java.util.Map

17.网页表单自动填充更改

与 WebView 对象相关的下列函数已经发生变化:
WebSettings:
getSaveFormData() 函数现在返回 false。之前,此函数返回 true。
调用 setSaveFormData() 不再有任何效果
WebViewDatabase:
调用 clearFormData() 不再有任何效果。
hasFormData() 函数现在返回 false。之前,当表单包含数据时,此函数返回 true。

18.WebView API API

Android 8.0 提供多种 API,帮助您管理在应用中显示网页内容的 WebView 对象。这些 API 可增强应用的稳定性和安全性,它们包括:
Version API
Google SafeBrowsing API
Termination Handle API
Renderer Importance API

19.关于 调用Collections.sort() 函数的影响

在 Android 8.0 中,Collections.sort() 是在 List.sort() 的基础上实现的。在 Android 7.x(API 级别 24 和 25)中,则恰恰相反。在过去,List.sort() 的默认实现会调用 Collections.sort()。
重写List.sort()方法时不能调用 Collections.sort(),因为这会导致堆栈因无限递归而溢出。相反,如果您需要 List 实现的默认行为,应避免重写 sort()。

20.后台执行限制API

为提高电池续航时间而引入的变更之一,当应用进入已缓存状态时,如果没有活动的组件,系统将解除应用具有的所有唤醒锁。
此外,为提高设备性能,系统会限制未在前台运行的应用的某些行为。具体而言:
现在,在后台运行的应用对后台服务的访问受到限制。
应用无法使用其清单注册大部分隐式广播(即,并非专门针对此应用的广播)。
默认情况下,这些限制仅适用于针对 O 的应用。不过,用户可以从 Settings 屏幕为任意应用启用这些限制,即使应用并不是以 O 为目标平台。
Android 8.0 还对特定函数做出了以下变更:
如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。
Demo
这个Demo是拿蓝牙扫描功能后台省电执行的例子,也可以作为蓝牙的参考Demo

21.后台位置限制 API

为节约电池电量、保持良好的用户体验和确保系统健康运行,在运行 Android 8.0 的设备上使用后台应用时,降低了后台应用接收位置更新的频率。此行为变更会影响包括 Google Play 服务在内的所有接收位置更新的应用。
此类变更会影响以下 API:
Fused Location Provider (FLP)
Geofencing
GNSS Measurements
Location Manager
Wi-Fi Manager
为确保您的应用按预期方式运行,请完成以下步骤:
查看您的应用的逻辑,并确保您使用的是最新的位置 API。
测试您的应用是否在每个用例中都表现出预期行为。
考虑使用 Fused Location Provider (FLP) 或地理围栏来处理依赖于用户当前位置的用例。
Demo

22.智能共享API

Android 8.0 了解用户的个性化分享首选项,在通过哪些应用分享各个类型的内容方面,也有着更好的把握。例如,如果用户为一张收据拍照,Android 8.0 可以建议费用跟踪应用;如果用户自拍,一款社交媒体应用可以更好地处理图像。Android 8.0 可以根据用户的个性化首选项自动学习所有这些模式。
智能分享适用于 image 之外的内容类型,例如 audio、video、text 和 URL 等。
要启用智能分享,请将具有最多三个字符串注释的 ArrayList 添加到分享内容的 intent。这些注释应说明内容中的主要部分或主题。下面的代码示例显示了如何向 intent 添加注释:

ArrayList<String> annotations = new ArrayList<>();
    annotations.add("selfie");//关于自拍的。
    annotations.add("holiday");//关于假期的,如假期旅行啥的风景照。例如添加这个标签分享的图片,用户首选项就是微信朋友圈
    annotations.add("document");//关于文件的,如海报,文档等。比如添加了这个标签分享的图片,用户首选项就是邮件。
    intent.putStringArrayListExtra(
        Intent.EXTRA_CONTENT_ANNOTATIONS,
        annotations
    );

这个是需要机器提前学习的,机器会记录下在某个标签下,用户的首选项,下次就会根据这个标签把这个应用排序在最前面。
已试验,同为text分享,分别添加两个不同的标签参数,分享时,没有区分标签对待,还是按照原来的一个类型,记录上次选择事件。

新的 StrictMode 检测程序

Android 8.0 添加了三个新的 StrictMode 检测程序,帮助识别应用可能出现的错误:
detectUnbufferedIo() 将检测您的应用何时读取或写入未缓冲的数据,这可能极大影响性能。
detectContentUriWithoutPermission() 将检测您的应用在其外部启动 Activity 时何时意外忘记向其他应用授予权限。
detectUntaggedSockets() 将检测您的应用何时使用网络流量,而不使用 setThreadStatsTag(int) 将流量标记用于调试目的。

缓存数据

Android 8.0 优化了缓存数据的导航和行为。现在,每个应用均获得一定的磁盘空间配额,用于存储 getCacheQuotaBytes(UUID) 返回的缓存数据。
当系统需要释放磁盘空间时,将开始从超过配额最多的应用中删除缓存文件。因此,如果将您的缓存数据量始终保持低于配额的水平,则在必须清除系统中的某些文件时,您的缓存文件将能坚持到最后。系统在决定删除您的应用中的哪些缓存文件时,将首先考虑删除最旧的文件(由修改时间确定)。
您还可以针对每个目录启用两种新行为,以控制系统如何释放缓存数据:
StorageManager.setCacheBehaviorAtomic() 可用于指示某个目录及其所有内容应作为一个不可分割的整体进行删除。
setCacheBehaviorTombstone(File, boolean) 可用于指示不应删除某个目录内的文件,而应将它们截断到 0 字节长度,使空文件保持完好。
最后,在需要为大文件分配磁盘空间时,可考虑使用新的 allocateBytes(FileDescriptor, long) API,它将自动清除属于其他应用的缓存文件(根据需要),以满足您的请求。在确定设备是否有足够的磁盘空间保存您的新数据时,请调用 getAllocatableBytes(UUID) 而不要使用 getUsableSpace(),因为前者会考虑系统要为您清除的任何缓存数据。

内容提供程序分页

我们已更新内容提供程序以支持加载大型数据集,每次加载一页。例如,一个具有大量图像的照片应用可查询要在页面中显示的数据的子集。内容提供程序返回的每个结果页面由一个 Cursor 对象表示。客户端和提供程序必须实现分页才能利用此功能。
如需了解有关内容提供程序变更的详细信息,请参阅 ContentProvider 和 ContentProviderClient。

内容刷新请求

现在,ContentProvider 和 ContentResolver 类均包含 refresh() 函数,这样,客户端可以更轻松地知道所请求的信息是否为最新信息。
您可以扩展 ContentProvider 以添加自定义的内容刷新逻辑。请务必重写 refresh() 函数,以返回 true,告知提供程序的客户端您已尝试自行刷新数据。
您的客户端应用可通过调用另一个函数(又称 refresh()),显式请求已刷新的内容。在调用此函数时,传入待刷新数据的 URI。
注:由于您可能通过网络不断请求数据,您应仅在有明显迹象表明内容确已过时时才从客户端调用 refresh()。执行此类内容刷新最常见的原因是响应滑动刷新手势,该手势显式请求当前界面显示最新内容。

JobScheduler 改进

Android 8.0 引入了对 JobScheduler 的多项改进。由于您通常可以使用计划作业替代现在受限的后台服务或隐式广播接收器,这些改进可以让您的应用更轻松地符合新的后台执行限制。
JobScheduler 的更新包括:
您现在可以将工作队列与计划作业关联。要将一个工作项添加到作业的队列中,请调用 JobScheduler.enqueue()。当作业运行时,它可以将待定工作从队列中剥离并进行处理。这种功能可以处理之前需要启动后台服务(尤其是实现 IntentService 的服务)的许多用例。
您现在可以通过调用 JobInfo.Builder.setClipData() 的方式将 ClipData 与作业关联。利用此选项,您可以将 URI 权限授予与作业关联,类似于这些权限传递到 Context.startService() 的方式。您也可以将 URI 权限授予用于工作队列上的 intent。
计划作业现在支持多个新的约束条件:
JobInfo.isRequireStorageNotLow()
如果设备的可用存储空间非常低,作业将不会运行。
JobInfo.isRequireBatteryNotLow()
如果电池电量等于或低于临界阈值,作业将不会运行;临界阈值是指设备显示 Low battery warning 系统对话框的电量。
NETWORK_TYPE_METERED
作业需要一个按流量计费的网络连接,比如大多数移动数据网络数据套餐。
自定义数据存储

媒体增强功能:API

添加新类VolumeShaper,可以用它来执行简短的自动音量转换,例如淡入、淡出和交叉淡入淡出。
音频焦点增强功能
媒体指标
MediaPlayer 类添加了多种新函数
音频录制器
音频播放控制
增强的媒体文件访问功能API