Android 8.1 功能和 API

Android 8.1(API 级别 27)为用户和开发者引入了各种新功能。本文档重点介绍了面向开发者的新变化。

Android Oreo(Go 版本)

Android Go 是一项旨在为全球数十亿网络用户优化 Android 体验的计划。从 Android 8.1 开始,我们将 Android 打造成适合入门级设备的理想平台。Android Oreo(Go 版本)配置中的功能包括:

  • 内存优化。改进了整个平台的内存用量,以确保应用可在 RAM 不超过 1GB 的设备上高效运行。
  • 灵活的定位选项。新的硬件功能常量,可让您通过 Google Play 将应用分发到正常 RAM 或低 RAM 设备。
  • Google Play。虽然所有应用都可以在搭载 Android Oreo(Go 版本)的设备上使用,但 Google Play 会按照“为数十亿用户打造产品” 指南,让开发者专门优化应用为数十亿用户提供出色的体验。

我们更新了“为数十亿用户打造产品” 指南,添加了有关如何 针对搭载 Android Oreo(Go 版本)的设备优化应用的指导。对于大多数开发者来说,优化现有 APK 或使用 Google Play 的 多 APK 功能将某个 APK 版本定位到低 RAM 设备是准备搭载 Android Oreo(Go 版本)的设备的最佳方式。请注意,无论使用何种设备,将应用打造 更精简、更高效都对您的所有受众群体都有好处。

Neural Networks API

Neural Networks API 为设备上的机器学习框架提供加速计算和推断功能,例如 Google 面向移动设备的跨平台机器学习库 TensorFlow Lite 以及 Caffe2 等。如需获取下载内容和文档,请访问 TensorFlow Lite 开源代码库。TensorFlow Lite 可与 Neural Networks API 配合使用,在移动设备上高效地运行 MobileNetsInception v3 智能回复等模型。

自动填充框架更新

Android 8.1(API 级别 27)对自动填充框架进行了多项改进,您可以将这些改进整合到您的应用中。

BaseAdapter 类现在包含 setAutofillOptions() 方法,该方法允许您在适配器中提供值的字符串表示法。这对于在其适配器中动态生成值的旋转图标控件非常有用。例如,您可以使用 setAutofillOptions() 方法以字符串形式提供年份列表,供用户在信用卡失效日期中选择。自动填充服务可以使用字符串表示法适当填充需要数据的视图。

此外,AutofillManager 类还包含 notifyViewVisibilityChanged(View, int, boolean) 方法,您可以通过调用该方法将虚拟结构中视图可见性的变化通知框架。对于非虚拟结构,该方法还有过载。不过,对于非虚拟结构,由于 View 类已调用该方法,因此通常无需您明确通知框架。

Android 8.1 还在 SaveInfo 中添加了对 CustomDescription and Validator 的支持,使自动填充服务能够更灵活地自定义保存界面功能。

自定义说明有助于自动填充服务说明正在保存的内容;例如,当屏幕包含信用卡时,它可以显示信用卡银行的徽标、信用卡号的最后四位数以及失效号。如需了解详情,请参阅 CustomDescription 类。

Validator 对象用于避免在不符合验证器条件时显示自动填充保存界面。如需了解详情,请参阅 Validator 类及其子类 LuhnChecksumValidatorRegexValidator

通知

Android 8.1 包含对通知的以下更改:

EditText 更新

从 API 级别 27 开始,EditText.getText() 方法会返回 Editable;而之前它会返回 CharSequence。此变更可向后兼容,因为 Editable 会实现 CharSequence

Editable 接口提供重要的附加功能。例如,由于 Editable 也实现了 Spannable 接口,因此您可以对 EditText 实例中的内容应用标记。

程序化安全浏览操作

通过使用 Safe Browsing API 的 WebView 实现,您的应用可以检测到 WebView 的实例何时尝试导航到 Google 已归类为已知威胁的网址。默认情况下,WebView 会显示一个插页,以警告用户存在已知威胁。通过该界面,用户可以选择仍然加载网址或返回到上一个安全的页面。

在 Android 8.1 中,您可以通过编程方式定义应用如何响应已知威胁:

  • 您可以控制应用是否向安全浏览功能报告已知威胁。
  • 您可以让应用在每次遇到被安全浏览功能归类为已知威胁的网址时都自动执行特定操作(例如返回到安全状态)。

注意:为了针对已知威胁提供最佳保护,请等到安全浏览功能完成初始化之后再调用 WebView 对象的 loadUrl() 方法。

以下代码段展示了如何指示应用的 WebView 实例在遇到已知威胁后始终返回安全状态:

AndroidManifest.xml

<manifest>
    <application>
        ...
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                   android:value="true" />
    </application>
</manifest>

MyWebActivity.java

Kotlin

private var superSafeWebView: WebView? = null
private var safeBrowsingIsInitialized: Boolean = false

// ...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    superSafeWebView = WebView(this).apply {
        webViewClient = MyWebViewClient()
        safeBrowsingIsInitialized = false
        startSafeBrowsing(this@SafeBrowsingActivity, { success ->
            safeBrowsingIsInitialized = true
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!")
            }
        })
    }
}

Java

private WebView superSafeWebView;
private boolean safeBrowsingIsInitialized;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    superSafeWebView = new WebView(this);
    superSafeWebView.setWebViewClient(new MyWebViewClient());
    safeBrowsingIsInitialized = false;

    superSafeWebView.startSafeBrowsing(this, new ValueCallback<Boolean>() {
        @Override
        public void onReceiveValue(Boolean success) {
            safeBrowsingIsInitialized = true;
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
            }
        }
    });
}

MyWebViewClient.java

Kotlin

class MyWebViewClient : WebViewClient() {
    // Automatically go "back to safety" when attempting to load a website that
    // Safe Browsing has identified as a known threat. An instance of WebView
    // calls this method only after Safe Browsing is initialized, so there's no
    // conditional logic needed here.
    override fun onSafeBrowsingHit(
            view: WebView,
            request: WebResourceRequest,
            threatType: Int,
            callback: SafeBrowsingResponse
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true)
        Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
    }
}

Java

public class MyWebViewClient extends WebViewClient {
    // Automatically go "back to safety" when attempting to load a website that
    // Safe Browsing has identified as a known threat. An instance of WebView
    // calls this method only after Safe Browsing is initialized, so there's no
    // conditional logic needed here.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponse callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        callback.backToSafety(true);
        Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                Toast.LENGTH_LONG).show();
    }
}

视频缩略图提取器

MediaMetadataRetriever 类包含一个新的方法 getScaledFrameAtTime(),该方法会查找给定时间位置附近的帧,并返回宽高比与源帧相同的位图,但会进行缩放以适应具有指定宽度和高度的矩形。这对于从视频生成缩略图非常有用。

我们建议使用此方法而不是 getFrameAtTime(),因为后者会返回与源视频具有相同分辨率的位图,因此会浪费内存。例如,4K 视频中的一帧将为 16MB 的位图,远大于您所需的缩略图大小。

Shared memory API

Android 8.1(API 级别 27)引入了一个新的 SharedMemory API。借助此类,您可以创建、映射和管理匿名 SharedMemory 实例。您可以在 SharedMemory 对象上设置内存保护,以便读取和/或写入数据;而且,由于 SharedMemory 对象是 Parcelable,因此您可以通过 AIDL 轻松将其传递给其他进程。

SharedMemory API 可与 NDK 中的 ASharedMemory 工具互操作。ASharedMemory 授予对文件描述符的访问权限,然后可以将其映射为读取和写入。它是在应用之间或单个应用中的多个进程之间共享大量数据的好方法。

WallpaperColors API

Android 8.1(API 级别 27)允许动态壁纸向系统界面提供颜色信息。您可以通过基于位图、可绘制对象或使用三种手动选择的颜色创建 WallpaperColors 对象来实现此目的。您还可以检索此颜色信息。

如需创建 WallpaperColors 对象,请执行以下任一操作:

  • 如需使用三种颜色创建 WallpaperColors 对象,请通过传递主色、辅色和第三色来创建 WallpaperColors 类的实例。主要颜色不得为 null。
  • 如需基于位图创建 WallpaperColors 对象,请通过传递位图来源作为参数来调用 fromBitmap() 方法。
  • 如需基于可绘制对象创建 WallpaperColors 对象,请通过传递可绘制对象来源作为参数来调用 fromDrawable() 方法。

如需从壁纸中检索主要、次要或第三颜色细节,请调用以下方法:

如需通知系统动态壁纸的颜色发生显著变化,请调用 notifyColorsChanged() 方法。此方法会触发 onComputeColors() 生命周期事件,让您有机会提供新的 WallpaperColors 对象。

如需添加颜色变化监听器,您可以调用 addOnColorsChangedListener() 方法。您还可以调用 getWallpaperColors() 方法来检索壁纸的主要颜色。

指纹更新

FingerprintManager 类引入了以下错误代码:

  • FINGERPRINT_ERROR_LOCKOUT_PERMANENT - 用户尝试使用指纹读取器解锁设备的次数过多。
  • FINGERPRINT_ERROR_VENDOR - 发生了特定于供应商的指纹读取器错误。

加密更新

Android 8.1 在加密方面进行了多次更改。

  • 在 Conscrypt 中实现了新的算法。系统会优先使用 Conscrypt 实现,而不是现有的 Bouncy Castle 实现。新算法包括:
    • AlgorithmParameters:GCM
    • KeyGenerator:AES
    • KeyGenerator:DESEDE
    • KeyGenerator:HMACMD5
    • KeyGenerator:HMACSHA1
    • KeyGenerator:HMACSHA224
    • KeyGenerator:HMACSHA256
    • KeyGenerator:HMACSHA384
    • KeyGenerator:HMACSHA512
    • SecretKeyFactory:DESEDE
    • Signature:NONEWITHECDSA
  • Cipher.getParameters().getParameterSpec(IvParameterSpec.class) 不再适用于使用 GCM 的算法。请改用 getParameterSpec(GCMParameterSpec.class)
  • 许多与传输层安全协议 (TLS) 相关的内部 Conscrypt 类都进行了重构。由于开发者有时会以反射方式访问这些库,因此保留了 shim 以支持以前的使用,但一些细节发生了变化。例如,套接字以前为 OpenSSLSocketImpl 类型,但现在为 ConscryptFileDescriptorSocketConscryptEngineSocket 类型,这两种类型都扩展 OpenSSLSocketImpl
  • 过去在传递 null 引用时,SSLSession 方法会抛出 IllegalArgumentException,现在会抛出 NullPointerException
  • RSA KeyFactory 不再允许从大于编码密钥的字节数组生成密钥。如果调用 generatePrivate()generatePublic() 时提供的 KeySpec 键结构未填满整个缓冲区,则会导致 InvalidKeySpecException
  • 过去,当套接字读取因套接字被关闭而中断时,Conscrypt 会从读取中返回 -1。现在,读取会抛出 SocketException
  • 根 CA 证书集已更改,主要移除了大量过时的证书,但还移除了 WoSign 和 StartCom 的根证书。如需详细了解此决定,请参阅 Google 安全博文:最终移除对 WoSign 和 StartCom 证书的信任