Android7(N)开发者应该知道的一切(最全)

Android N for Developers

转载请注明: http://blog.csdn.net/wen_demo/article/details/51943340

 

1.多窗口支持


Android N 添加了对同时显示多个应用窗口的支持。 在手持设备上,两个应用可以在“分屏”模式中左右并排或上下并排显示。 在电视设备上,应用可以使用“画中画”模式,在用户与另一个应用交互的同时继续播放视频。

如果您使用 N Preview SDK 构建应用,则可以配置应用处理多窗口显示的方法。 例如,您可以指定 Activity 的最小允许尺寸。 您还可以禁用应用的多窗口显示,确保系统仅以全屏模式显示应用。

概览


Android N 允许多个应用同时共享屏幕。例如,用户可以分屏显示应用,在左边查看网页,同时在右边写邮件。 用户体验取决于设备:

  • 运行 Android N 的手持设备具有分屏模式。 在此模式中,系统以左右并排或上下并排的方式分屏显示两个应用。 用户可以拖动两个应用之间的分界线,放大其中一个应用,同时缩小另一个。
  • 在运行 Android N 的 Nexus Player 上,应用能以画中画模式显示,即在用户浏览网页或与其他应用交互的同时继续显示内容。
  • 较大设备的制造商可选择启用自由形状模式,在该模式中,用户可以自由调整各 Activity 的尺寸。 若制造商启用此功能,设备将同时具有自由形状模式和分屏模式。

图 1. 两个应用在分屏模式中左右并排显示。

用户可以通过以下方式切换到多窗口模式:

  • 若用户打开 Overview 屏幕并长按 Activity 标题,则可以拖动该 Activity 至屏幕突出显示的区域,使 Activity 进入多窗口模式。
  • 若用户长按 Overview 按钮,设备上的当前 Activity 将进入多窗口模式,同时将打开 Overview 屏幕,用户可在该屏幕中选择要共享屏幕的另一个 Activity。

用户可以在两个 Activity 共享屏幕的同时在这两个 Activity 之间拖放数据 (在此之前,用户只能在一个 Activity 内部拖放数据)。

多窗口生命周期


多窗口模式不会更改 Activity 生命周期

在多窗口模式中,在指定时间只有最近与用户交互过的 Activity 为活动状态。 该 Activity 将被视为顶级 Activity。 所有其他 Activity 虽然可见,但均处于暂停状态。 但是,这些已暂停但可见的 Activity 在系统中享有比不可见 Activity 更高的优先级。 如果用户与其中一个暂停的 Activity 交互,该 Activity 将恢复,而之前的顶级 Activity 将暂停。

注:在多窗口模式中,用户仍可以看到处于暂停状态的应用。 应用在暂停状态下可能仍需要继续其操作。 例如,处于暂停模式但可见的视频播放应用应继续显示视频。 因此,我们建议播放视频的 Activity 不要暂停其 onPause() 处理程序中的视频。 应暂停 onStop() 中的视频,并恢复 onStart()中的视频播放。

处理运行时变更中所述,用户使用多窗口模式显示应用时,系统将通知 Activity 发生配置变更。 这也会发生在当用户调整应用大小,或将应用恢复到全屏模式时。 该变更与系统通知应用设备从纵向模式切换到横向模式时的 Activity 生命周期影响基本相同,但设备不仅仅是交换尺寸,而是会变更尺寸。 如处理运行时变更中所述,您的 Activity 可以自行处理配置变更,或允许系统销毁 Activity,并以新的尺寸重新创建该 Activity。

如果用户调整窗口大小,并在任意维度放大窗口尺寸,系统将调整 Activity 以匹配用户操作,同时根据需要发布运行时变更。 如果应用在新公开区域的绘制滞后,系统将使用 windowBackground 属性或默认 windowBackgroundFallback 样式属性指定的颜色暂时填充该区域。

针对多窗口模式配置应用


如果您的应用面向 Android N,您可以对应用的 Activity 是否支持多窗口显示以及显示方式进行配置。 您可以在清单文件中设置属性,以控制大小和布局。 根 Activity 的属性设置适用于其任务栈中的所有 Activity。 例如,如果根 Activity 已 android:resizeableActivity 设定为 true,则任务栈中的所有 Activity 都将可以调整大小。

注:如果您使用低于 Android N 版本的 SDK 构建多向应用,则用户在多窗口模式中使用应用时,系统将强制调整应用大小。 系统将显示对话框,提醒用户应用可能会发生异常。 系统不会调整定向应用的大小;如果用户尝试在多窗口模式下打开定向应用,应用将全屏显示。

android:resizeableActivity

在清单的 <activity> 或 <application> 节点中设置该属性,启用或禁用多窗口显示:

android:resizeableActivity=["true" | "false"]
  
  

如果该属性设置为 true,Activity 将能以分屏和自由形状模式启动。 如果此属性设置为 false,Activity 将不支持多窗口模式。 如果该值为 false,且用户尝试在多窗口模式下启动 Activity,该 Activity 将全屏显示。

如果您的应用面向 Android N,但未对该属性指定值,则该属性的值默认设为 true。

android:supportsPictureInPicture

在清单文件的 <activity> 节点中设置该属性,指明 Activity 是否支持画中画显示。 如果 android:resizeableActivity 为 false,将忽略该属性。

android:supportsPictureInPicture=["true" | "false"]
  
  

布局属性

对于 Android N,<layout> 清单元素支持以下几种属性,这些属性影响 Activity 在多窗口模式中的行为:

android:defaultWidth
以自由形状模式启动时 Activity 的默认宽度。
android:defaultHeight
以自由形状模式启动时 Activity 的默认高度。
android:gravity
以自由形状模式启动时 Activity 的初始位置。请参阅  Gravity 参考资料,了解合适的值设置。
android:minimalHeightandroid:minimalWidth
分屏和自由形状模式中 Activity 的最小高度和最小宽度。 如果用户在分屏模式中移动分界线,使 Activity 尺寸低于指定的最小值,系统会将 Activity 裁剪为用户请求的尺寸。

例如,以下节点显示了如何指定 Activity 在自由形状模式中显示时 Activity 的默认大小、位置和最小尺寸:

<activity android:name=".MyActivity">
    <layout android:defaultHeight="500dp"
          android:defaultWidth="600dp"
          android:gravity="top|end"
          android:minimalHeight="450dp"
          android:minimalWidth="300dp" />
</activity>
  
  

在多窗口模式中运行应用


Android N 添加了新功能,以支持可在多窗口模式中运行的应用。

多窗口模式中被禁用的功能

在设备处于多窗口模式中时,某些功能会被禁用或忽略,因为这些功能对与其他 Activity 或应用共享设备屏幕的 Activity 而言没有意义。 此类功能包括:

  • 某些系统 UI 自定义选项将被禁用;例如,在非全屏模式中,应用无法隐藏状态栏。
  • 系统将忽略对 android:screenOrientation 属性所作的更改。

多窗口变更通知和查询

Activity 类中添加了以下新方法,以支持多窗口显示。 有关各方法的详细信息,请参阅 N Preview SDK 参考

Activity.isInMultiWindowMode()
调用该方法以确认 Activity 是否处于多窗口模式。
Activity.isInPictureInPictureMode()
调用该方法以确认 Activity 是否处于画中画模式。

注:画中画模式是多窗口模式的特例。 如果 myActivity.isInPictureInPictureMode() 返回 true,则 myActivity.isInMultiWindowMode() 也返回 true。

Activity.onMultiWindowModeChanged()
Activity 进入或退出多窗口模式时系统将调用此方法。 在 Activity 进入多窗口模式时,系统向该方法传递 true 值,在退出多窗口模式时,则传递 false 值。
Activity.onPictureInPictureModeChanged()
Activity 进入或退出画中画模式时系统将调用此方法。 在 Activity 进入画中画模式时,系统向该方法传递 true 值,在退出画中画模式时,则传递 false 值。

每个方法还有 Fragment 版本,例如 Fragment.isInMultiWindowMode()

进入画中画模式

如需在画中画模式中启动 Activity,请调用新方法 Activity.enterPictureInPictureMode()。 如果设备不支持画中画模式,则此方法无效。 如需了解详细信息,请参阅画中画文档。

在多窗口模式中启动新 Activity

在启动新 Activity 时,用户可以提示系统如果可能,应将新 Activity 显示在当前 Activity 旁边。 要执行此操作,可使用标志Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT。 传递此标志将请求以下行为:

  • 如果设备处于分屏模式,系统会尝试在启动系统的 Activity 旁创建新 Activity,这样两个 Activity 将共享屏幕。 系统并不一定能实现此操作,但如果可以,系统将使两个 Activity 处于相邻的位置。
  • 如果设备不处于分屏模式,则该标志无效。

如果设备处于自由形状模式,则在启动新 Activity 时,用户可通过调用 ActivityOptions.setLaunchBounds() 指定新 Activity 的尺寸和屏幕位置。 如果设备不处于多窗口模式,则该方法无效。

注:如果您在任务栈中启动 Activity,该 Activity 将替换屏幕上的 Activity,并继承其所有的多窗口属性。 如果要在多窗口模式中以单独的窗口启动新 Activity,则必须在新的任务栈中启动此 Activity。

支持拖放

用户可以在两个 Activity 共享屏幕的同时在这两个 Activity 之间拖放数据 (在此之前,用户只能在一个 Activity 内部拖放数据)。 因此,如果您的应用目前不支持拖放功能,您可以在其中添加此功能。

N Preview SDK 扩展了 android.view 软件包,以支持跨应用拖放。 有关以下类和方法的详细信息,请参阅 N Preview SDK 参考

android.view.DropPermissions
令牌对象,负责指定对接收拖放数据的应用授予的权限。
View.startDragAndDrop()
View.startDrag() 的新别名。要启用跨 Activity 拖放,请传递新标志  View.DRAG_FLAG_GLOBAL。 如需对接收拖放数据的 Activity 授予 URI 权限,可根据情况传递新标志  View.DRAG_FLAG_GLOBAL_URI_READ 或  View.DRAG_FLAG_GLOBAL_URI_WRITE
View.cancelDragAndDrop()
取消当前正在进行的拖动操作。只能由发起拖动操作的应用调用。
View.updateDragShadow()
替换当前正在进行的拖动操作的拖动阴影。只能由发起拖动操作的应用调用。
Activity.requestDropPermissions()
请求使用  DragEvent 中包含的  ClipData 传递的内容 URI 的权限。

测试应用的多窗口支持


无论您是否针对 Android N 更新应用,都应验证应用在多窗口模式下的行为,以防用户尝试在运行 Android N 的设备上以多窗口模式启动应用。

配置测试设备

如果在设备上安装 Android N,则将自动支持分屏模式。

如果应用并非使用 N Preview SDK 构建

如果您的应用不是使用 N Preview SDK 构建的,则用户尝试在多窗口模式中使用应用时,系统将强制调整应用大小,除非应用进行了定向声明。

如果您的应用没有进行定向声明,则应在运行 Android N 的设备上启动应用,并尝试将应用切换到分屏模式。 验证并确保在强制调整应用大小时用户体验可接受。

如果应用进行了定向声明,则应尝试将应用切换到多窗口模式。 验证并确保执行此操作后,应用仍保持全屏模式。

如果支持多窗口模式

如果您的应用是使用 N Preview SDK 构建的,且未禁用多窗口支持,则分别在分屏和自由形状模式下验证以下行为。

  • 在全屏模式下启动应用,然后通过长按 Overview 按钮切换到多窗口模式。 验证并确保应用正常切换。
  • 直接在多窗口模式中启动应用,验证并确保应用正常启动。 您可以按一下 Overview 按钮,再长按应用的标题栏,并将其拖动到屏幕上任一突出显示的区域,从而在多窗口模式中启动应用。
  • 拖动分界线,在分屏模式中调整应用的大小。 验证并确保应用正常调整大小且未崩溃,并且必要的 UI 元素仍可见。
  • 如果您指定了应用的最小尺寸,请尝试将应用尺寸调整到低于最小值。 验证并确保无法将应用尺寸调整到低于指定最小值。
  • 完成所有测试后,验证并确保应用性能可以接受。例如,验证并确保调整应用大小后更新 UI 没有长时间的滞后。
测试检查单

要在多窗口模式中验证应用性能,请执行以下操作。 除非另有说明,否则请分别在分屏和多窗口模式中执行以下操作。

  • 进入和退出多窗口模式。
  • 从您的应用切换到另一个应用,验证并确保应用在非活动但可见的状态下正常运行。 例如,如果您的应用在播放视频,则验证并确保在用户与另一个应用交互时视频仍在继续播放。
  • 在分屏模式中,尝试移动分界线,放大或缩小应用。 分别在左右和上下并排显示模式中尝试这些操作。 验证并确保应用不会崩溃,主要功能可见,且调整操作不需要过长时间。
  • 快速连续执行几次调整操作。验证并确保应用不会崩溃或出现内存泄漏。 有关检查应用内存使用率的信息,请参阅查看内存使用率
  • 在多个不同窗口配置中正常使用应用,验证并确保应用正常运行。 验证并确保文本可读,且 UI 元素大小正常,不影响交互。

如果已禁用多窗口支持

如果您通过设置 android:resizableActivity="false" 禁用了多窗口支持,则应在运行 Android N 的设备上启动应用,并尝试将应用切换到自由形状和分屏模式。 验证并确保执行此操作后,应用仍保持全屏模式。


2.通知栏


Android N 引入了一些新 API,允许应用发布具有高度可见性和交互性的通知。

Android N 扩展了现有 RemoteInput 通知 API,以支持手持式设备上的内联回复。 此功能允许用户从通知栏快速进行回复,无需访问应用。

此外,Android N 还允许捆绑类似的通知并将它们显示为一则通知。 为了实现此功能,Android N 使用现有的NotificationCompat.Builder.setGroup() 方法。用户可以从通知栏展开各通知,并分别对每则通知进行回复和清除等操作。

最后,Android N 还添加了一些新 API,允许您在应用的自定义通知视图中使用系统装饰元素。 这些 API 可帮助确保通知视图与标准模板的展示效果相一致。

本文重点介绍您在应用中使用新通知功能时应加以考虑的一些重要变更。

直接回复


利用 Android N 中的直接回复功能,用户可以直接在通知界面内快速回复短信或更新任务列表。 在手持式设备上,可通过通知中另外附加的按钮进行内联回复操作。 当用户通过键盘回复时,系统会将文本回复附加到您为通知操作指定的 Intent,并将 Intent 发送到手持式设备应用。

图 1.Android N 添加了 Reply 操作按钮。

添加内联回复操作

要创建支持直接回复的通知操作:

  1. 创建一个可添加到通知操作的 RemoteInput.Builder 实例。 该类的构造函数接受系统用作文本输入密钥的字符串。 之后,手持式设备应用使用该密钥检索输入的文本。
    // Key for the string that's delivered in the action's intent.
    private static final String KEY_TEXT_REPLY = "key_text_reply";
    String replyLabel = getResources().getString(R.string.reply_label);
    RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
            .setLabel(replyLabel)
            .build();
        
        
  2. 使用 addRemoteInput() 向操作附加 RemoteInput 对象。
    // Create the reply action and add the remote input.
    Notification.Action action =
            new Notification.Action.Builder(R.drawable.ic_reply_icon,
                    getString(R.string.label), replyPendingIntent)
                    .addRemoteInput(remoteInput)
                    .build();
        
        
  3. 对通知应用操作并发出通知。
    // Build the notification and add the action.
    Notification newMessageNotification =
            new Notification.Builder(mContext)
                    .setSmallIcon(R.drawable.ic_message)
                    .setContentTitle(getString(R.string.title))
                    .setContentText(getString(R.string.content))
                    .addAction(action))
                    .build();
    
    // Issue the notification.
    NotificationManager notificationManager =
            NotificationManager.from(mContext);
    notificationManager.notify(notificationId, newMessageNotification);
    
        
        

在触发通知操作时系统提示用户输入回复。


图 2.用户从通知栏输入文本。

从内联回复检索用户输入

要从通知界面接收用户输入并发送到在回复操作的 Intent 中声明的 Activity:

  1. 通过传递通知操作的 Intent 作为输入参数来调用 getResultsFromIntent()。 该方法返回含有文本回复的 Bundle
    Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
        
        
  2. 使用产生的密钥查询捆绑包(提供给 RemoteInput.Builder 构造函数)。以下代码段说明了方法如何从捆绑包检索输入文本:
    // Obtain the intent that started this activity by calling
    // Activity.getIntent() and pass it into this method to
    // get the associated string.
    
    private CharSequence getMessageText(Intent intent) {
        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
        if (remoteInput != null) {
            return remoteInput.getCharSequence(KEY_TEXT_REPLY);
        }
        return null;
     }
        
        
  3. 使用您为上一项通知提供的相同的通知 ID 来建立和发布另一项通知。 进度指示器从通知界面消失,以告知用户已回复成功。 在处理这项新通知时,使用被传递到接收器 onReceive() 方法的上下文。
    // Build a new notification, which informs the user that the system
    // handled their interaction with the previous notification.
    Notification repliedNotification =
            new Notification.Builder(context)
                    .setSmallIcon(R.drawable.ic_message)
                    .setContentText(getString(R.string.replied))
                    .build();
    
    // Issue the new notification.
    NotificationManager notificationManager =
            NotificationManager.from(context);
    notificationManager.notify(notificationId, repliedNotification);
        
        

对于交互式应用(例如聊天),这可以用来在处理检索到的文本时添加其他上下文。 例如,这些应用可以显示多行聊天记录。 当用户通过 RemoteInput回复时,您可以使用 setRemoteInputHistory() 方法更新回复历史。

在应用收到远程输入后,必须更新或取消通知。 如果用户使用直接回复来对远程更新进行回复,则不可取消通知。 否则,更新通知以显示用户的回复。对于使用 MessagingStyle 的通知,您应该添加回复来作为最新消息。 当使用其它模板时,您可以将用户的回复追加到远程输入历史。

捆绑通知


Android N 为开发者提供了表示通知队列的新方法: 捆绑通知。这类似于 Android Wear 中的通知堆栈功能。 例如,如果应用为接收的消息创建通知,那么在接收到多个消息时,应用会将通知捆绑在一起成为一个群组。 您可以使用现有的 Builder.setGroup() 方法捆绑类似的通知。

通知组对组内的通知施加层次结构。 层次结构的顶层是父级通知,其显示该群组的摘要信息。 用户可以逐步展开通知组,随着用户深入展开,系统将显示更多信息。 当用户展开捆绑包时,系统将显示其所有子通知的更多信息;当用户展开其中一则通知时,系统显示该通知的所有内容。


图 3.用户可以逐步展开通知组。

注:如果同一应用发送了四条或以上通知,并且未指定分组,系统会自动将它们分到一组。

如需了解如何将通知添加到组,请参阅将各通知添加到组

捆绑通知最佳做法

本节提供了有关何时使用通知组而非早期版本 Android 平台中的 InboxStyle 通知的指南。

何时使用捆绑通知

只有在您的用例满足以下所有条件时才应使用通知组:

  • 子通知为完整通知,且可以单独显示,无需群组摘要。
  • 单独显示子通知更合理。例如:
    • 子通知可操作,且每个子通知均有特定的操作。
    • 子通知中包含用户想要查看的更多信息。

好的通知组用例示例包括:显示传入消息列表的短信应用,或显示收到的电子邮件列表的电子邮件应用。

适合显示单一通知的用例示例包括:从某一个人收到的单独消息,或以列表表示的单行文本项目。 您可以使用 InboxStyle 或 BigTextStyle 实现此功能。

显示捆绑通知

即使组内仅含有一则子通知,应用也应发布组摘要。 如果只含有一则通知,系统将取消摘要并直接显示子通知。 这样可确保用户在滑动切换组内的子通知时,系统仍可以提供一致的用户体验。

注:本版本 Android N 目前还无法在仅含一则子通知时取消通知组的摘要。 我们将在之后版本的 Android N 中添加此功能。

扫视通知

虽然系统通常以群组的方式显示子通知,但您可以进行设置,使其暂时作为浮动通知显示。 该功能非常实用,因为其允许用户立即访问最近的子通知以及与其相关的操作。

后向兼容性

自 Android 5.0(API 级别 21)起,Notification API 中就添加了通知组和远程输入,以支持 Android Wear 设备。 如果您已经使用这些 API 构建通知,则只需验证应用行为是否符合上述指南,并考虑实现 setRemoteInputHistory()

为了支持后向兼容性,支持库的 NotificationCompat 类中提供了相同的 API,以便您构建可在早期 Android 版本中运行的通知。 在手持式设备和平板电脑上,用户只能看到摘要通知,因此应用应仍提供收件箱式或类似形式的通知显示模式,以显示群组的全部信息内容。 鉴于 Android Wear 设备允许用户查看所有子通知,包括更早级别平台上的通知,您应在不依赖 API 级别的基础上构建子通知。

自定义视图


从 Android N 开始,您将可以自定义通知视图,同时仍可以使用系统装饰元素,例如通知标头、操作和可展开的布局。

为启用该功能,Android N 添加了以下 API,以便您样式化自己的自定义视图:

DecoratedCustomViewStyle()
样式化除媒体通知外的其他通知。
DecoratedMediaCustomViewStyle()
样式化媒体通知。

如需使用这些新 API,可调用 setStyle() 方法,并向其传递所需的自定义视图样式。

此代码段显示了如何使用 DecoratedCustomViewStyle() 方法构建自定义通知对象。

Notification notification = new Notification.Builder()
           .setSmallIcon(R.drawable.ic_stat_player)
           .setLargeIcon(albumArtBitmap))
           .setCustomContentView(contentView);
           .setStyle(new Notification.DecoratedCustomViewStyle())
           .build();

  
  

消息传递样式


Android N 引入了一项新的 API 来自定义通知样式。 使用 MessageStyle 类,您可以更改在通知中显示的多个标签,包括会话标题、其他消息和通知的内容视图。

以下代码段演示了如何使用 MessageStyle 类来自定义通知样式。

  Notification notification = new Notification.Builder()
             .setStyle(new Notification.MessagingStyle("Me")
                 .setConversationTitle("Team lunch")
                 .addMessage("Hi", timestamp1, null) // Pass in null for user.
                 .addMessage("What's up?", timestamp2, "Coworker")
                 .addMessage("Not much", timestamp3, null)
                 .addMessage("How about lunch?", timestamp4, "Coworker"));
  
  


3.Data Saver


在智能手机的整个生命周期,蜂窝数据计划的成本通常会超出设备本身的成本。 在 N Developer Preview 中,用户可以在整个设备上启用 Data Saver,以减少流量消耗,无论是在漫游,账单周期即将结束,还是使用少量的预付费数据包。

当用户在 Settings 中启用 Data Saver 且设备位于按流量计费的网络上时,系统屏蔽后台流量消耗,同时指示应用在前台尽可能使用较少的数据。 用户可以将特定应用加入白名单以允许后台按流量计费的流量消耗,即使在打开 Data Saver 时也是如此。

N Developer Preview 扩展ConnectivityManager API,为应用提供检索用户的 Data Saver 首选项监控首选项变更的方式。 这被认为是应用检查用户是否启用了 Data Saver 并努力限制前台和后台流量消耗的有效方法。

检查 Data Saver 首选项


在 N Developer Preview 中,应用可以使用 ConnectivityManager API 来确定正在应用的是哪些流量消耗限制。 getRestrictBackgroundStatus()方法返回下列值之一:

RESTRICT_BACKGROUND_STATUS_DISABLED
Data Saver 已禁用。
RESTRICT_BACKGROUND_STATUS_ENABLED
用户已为此应用禁用 Data Saver。应用应努力限制前台流量消耗,并妥善处理后台流量消耗限制。
RESTRICT_BACKGROUND_STATUS_WHITELISTED
用户已启用 Data Saver,但应用在白名单中。应用应努力限制前台和后台流量消耗。

这被认为是在设备连接到按流量计费的网络时限制流量消耗的有效方法,即使 Data Saver 被禁用或应用在白名单中。 以下示例代码使用ConnectivityManager.isActiveNetworkMetered() 和 ConnectivityManager.getRestrictBackgroundStatus() 来确定应用应使用多少数据:

ConnectivityManager connMgr = (ConnectivityManager)
        getSystemService(Context.CONNECTIVITY_SERVICE);
// Checks if the device is on a metered network
if (connMgr.isActiveNetworkMetered()) {
  // Checks user’s Data Saver settings.
  switch (connMgr.getRestrictBackgroundStatus()) {
    case RESTRICT_BACKGROUND_STATUS_ENABLED:
    // Background data usage is blocked for this app. Wherever possible,
    // the app should also use less data in the foreground.

    case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
    // The app is whitelisted. Wherever possible,
    // the app should use less data in the foreground and background.

    case RESTRICT_BACKGROUND_STATUS_DISABLED:
    // Data Saver is disabled. Since the device is connected to a
    // metered network, the app should use less data wherever possible.
  }
} else {
  // The device is not on a metered network.
  // Use data as required to perform syncs, downloads, and updates.
}
  
  

请求白名单权限

如果您的应用需要使用后台数据,它可以通过发送一项包含您的应用软件包名称的 URI 的Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS Intent 来请求白名单权限:例如 package:MY_APP_ID

发送 Intent 和 URI 将启动 Settings 应用,还会显示您的应用的流量消耗设置。 用户随后可以决定是否启用应用的后台数据。 在您发送此 Intent 之前,先询问用户是否希望启用 Settings 应用,以启用后台流量消耗,这是一种有效的做法。

监控 Data Saver 首选项变更


应用可以通过创建一条 BroadcastReceiver 以侦听 ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED 以及使用 Context.registerReceiver()动态注册接收器来监控 Data Saver 首选项变更。 当应用接收到这条广播时,应通过调用 ConnectivityManager.getRestrictBackgroundStatus() 来检查新的 Data Saver 首选项是否会影响其权限

注:系统只会向使用 Context.registerReceiver() 进行动态注册的应用发送此广播。 在其清单中注册接收此广播的应用将不会收到它们。

使用 Android 调试桥命令测试


Android 调试桥 (ADB)  提供了多条命令,可用于检查和配置网络权限:
$ adb shell dumpsys netpolicy
生成包括当前全局后台网络限制设置、目前在白名单中的软件包 UID 以及其他已知软件包的权限的报告。
$ adb shell cmd netpolicy
显示网络政策管理器 (netpolicy) 命令的完整清单。
$ adb shell cmd netpolicy set restrict-background <boolean>
当分别传递  true 或  false 时,启用或禁用 Data Saver 模式。
$ adb shell cmd netpolicy add restrict-background-whitelist <UID>
将指定软件包 UID 加入白名单,以允许后台按流量计费的流量消耗 。
$ adb shell cmd netpolicy remove restrict-background-whitelist <UID>

从白名单中移除指定软件包 UID,以阻止当 Data Saver 启用时后台按流量计费的流量消耗。



4.TV 


TV 输入服务允许用户通过时移 API 暂停和继续频道播放。 Android N 通过允许用户保存多个录制的会话,扩展了时移。

用户可以提前安排录制,或在观看节目时开始录制。 系统保存录制后,用户即可使用系统 TV 应用浏览、管理和播放录制。

如果想要为 TV 输入服务提供录制功能,您必须指示系统您的应用支持录制,实现录制节目功能,处理和传达录制期间发生的所有错误,并管理录制的会话。

注:Live Channels 应用尚不提供可让用户创建或访问录制的方式。 在变更 Live Channels 应用之前,可能无法充分测试您的 TV 输入服务的录制体验。

指示支持录制


为了通知系统您的 TV 输入服务支持录制,请将您的服务元数据 XML 文件中的 android:canRecord 属性设置为 true

<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
  android:canRecord="true"
  android:setupActivity="com.example.sampletvinput.SampleTvInputSetupActivity" />
  
  

如需了解有关服务元数据文件的详细信息,请参阅在清单中声明您的 TV 输入

或者,您可以使用以下步骤在您的代码中表明录制支持:

  1. 在 TvInputService.onCreate() 方法中,使用 TvInputInfo.Builder 类创建一个新的 TvInputInfo 对象。
  2. 创建新的 TvInputInfo 对象时,在调用 build() 之前先调用 setCanRecord(true),以表明您的服务支持录制。
  3. 通过调用 TvInputManager.updateTvInputInfo() 在系统中注册 TvInputInfo 对象。

录制会话


TV 输入服务注册其支持录制功能后,系统会在需要访问应用的录制实现时调用 TvInputService.onCreateRecordingSession()。 实现您自己的TvInputService.RecordingSession 子类并在触发 onCreateRecordingSession() 回调后返回它。 此子类负责切换到正确的频道数据,录制请求的数据,以及向系统传达录制状态和错误。

系统调用在频道 URI 中传递的 RecordingSession.onTune() 时,将调到该 URI 指定的频道。 通过调用 notifyTuned() 通知系统应用已调到所需的频道,或者,如果应用无法调到正确的频道,请调用 notifyError()

系统接下来调用 RecordingSession.onStartRecording() 回调。您的应用必须立即开始录制。系统调用此回调时,它可能会提供一个 URI,其中包含有关将录制的节目的信息。 完成录制后,您需要将此数据复制到 RecordedPrograms 数据表。

最后,系统调用 RecordingSession.onStopRecording()。此时,您的应用必须立即停止录制。 您还需要在 RecordedPrograms 表中创建一个条目。 此条目应在 RecordedPrograms.COLUMN_RECORDING_DATA_URI 列中包含录制的会话数据 URI,以及在初始调用 onStartRecording() 时系统提供的任何节目信息。

如需了解有关如何访问 RecordedPrograms 表的更多详情,请参阅管理录制的会话

处理录制错误


如果录制期间发生错误,使录制的数据无法使用,则通过调用 RecordingSession.notifyError() 通知系统。同样,您可以在创建录制会话后调用notifyError(),以通知系统您的应用不再录制会话。

如果录制期间发生错误,但您想提供可使用的部分录制供用户播放,则调用 RecordingSession.notifyRecordingStopped() 以使系统可以使用部分会话。

管理录制的会话


系统在 TvContract.RecordedPrograms 内容提供程序表中维护来自所有支持录制的频道应用的所有已录制会话的信息。 此信息可通过RecordedPrograms.Uri 内容 URI 访问。使用内容提供程序 API 读取、添加和删除此表中的条目。

如需了解有关使用内容提供程序数据的详细信息,请参阅内容提供程序基本知识

最佳做法


TV 设备的存储可能有限,因此请运用您的最佳判断来分配存储,以保存录制的会话。 如果没有足够的空间存储录制的会话,请使用RecordingCallback.onError(RECORDING_ERROR_INSUFFICIENT_SPACE)

当用户发起录制时,您应尽快开始录制数据。 为便于执行此操作,请在系统调用 onCreateRecordingSession() 回调时完成前期的所有耗时任务,如访问和分配存储空间。 这样做让您能够在触发 onStartRecording() 回调时立即开始录制。



5.密匙 Key Attestation


密钥认证会给你更多的信心,你在使用你的应用程序 的密钥存储在设备的硬件支持的密钥库。下面的部分 描述如何验证性能的硬件支持的密钥和如何 解释认证证书的扩展数据模式。

检索和验证密钥对硬件支持


在密钥认证,你指定一个密钥对的别名。在返回的认证 工具,提供了一个证书链,您可以使用它来验证 ,密钥对的性质。

在这个链的根证书签名使用的认证密钥, 该设备制造商注入设备的硬件支持 密钥存储在。

笔记在设备与Android和谷歌播放 服务船,根证书是由谷歌发行。你应该确认 在谷歌的根证书列表出现这个根证书。

实施重点认证,完成以下步骤:

  1. 使用密钥库对象的getcertificatechain()得到一个参考的X.509证书与密钥相关的 硬件支持外链的方法。
  2. 通过检查每个证书的有效性CRL对象的isrevoked()方法

    警告:虽然你可以在你的应用程序 直接完成这个过程,它的安全检查证书吊销列表 单独的服务器上,你的信任。

  3. 创建一个认证对象,通过在 证书链作为参数的第一个元素:

    /“证书”包含一个特定的证书关联的密钥链
    / /对在设备的硬件支持的密钥库。
    attestationcert X509Certificate= X509Certificate证书]
    认证hardwarekeyattestation=  认证attestationcert
        
        

    认证对象提取扩展数据在这个证书 将这些信息存储在一个更方便的格式。更多的细节 扩展的数据模式,看证书扩展数据模式

  4. 在使用访问器方法认证类 从证书检索扩展数据。这些方法使用相同的名称和结构层次 在证书扩展数据 图式。

    例如,查看设备的TEE验证启动键,使用以下方法 序列:

    /“hardwarekeyattestation”包含了认证的第一要素
    / /证书链。
    authorizationlistteeauthlist=hardwarekeyattestationgetteeenforced()
    rootoftrustteerootoftrust=teeauthlistgetrootoftrust()
    字节[ ]teeverifiedbootkey=teerootoftrustgetverifiedbootkey()
        
        
  5. 从扩展数据比较认证与 设定的值,您期望的硬件支持的密钥包含对象。

    警告:虽然你可以在你的应用程序 直接完成这个过程,它的安全检查在一个单独的服务器,你信任证书的扩展数据 。

证书扩展数据模式


密钥认证验证扩展数据显示在第一 证书在设备的硬件支持的密钥库内的链。的 证书存储的信息根据ASN。1模式:

KeyDescription ::= SEQUENCE {
    attestationVersion  INTEGER,
    attestationSecurityLevel  SecurityLevel,
    keymasterVersion  INTEGER,
    keymasterSecurityLevel  SecurityLevel,
    attestationChallenge  OCTET_STRING,
    保留octet_string,
    softwareEnforced  AuthorizationList,
    teeEnforced  AuthorizationList,
}

SecurityLevel ::= ENUMERATED {
    Software  (0),
    TrustedEnvironment  (1),
}

AuthorizationList ::= SEQUENCE {
    purpose  [1] EXPLICIT SET OF INTEGER OPTIONAL,
    algorithm  [2] EXPLICIT INTEGER OPTIONAL,
    keySize  [3] EXPLICIT INTEGER OPTIONAL,
    digest  [5] EXPLICIT SET OF INTEGER OPTIONAL,
    padding  [6] EXPLICIT SET OF INTEGER OPTIONAL,
    ecCurve  [10] EXPLICIT INTEGER OPTIONAL,
    rsaPublicExponent  [200] EXPLICIT INTEGER OPTIONAL,
    activeDateTime  [400] EXPLICIT INTEGER OPTIONAL,
    originationExpireDateTime  [401] EXPLICIT INTEGER OPTIONAL,
    usageExpireDateTime  [402] EXPLICIT INTEGER OPTIONAL,
    noAuthRequired  [503] EXPLICIT NULL OPTIONAL,
    userAuthType  [504] EXPLICIT INTEGER OPTIONAL,
    authTimeout  [505] EXPLICIT INTEGER OPTIONAL,
    allowWhileOnBody  [506] EXPLICIT NULL OPTIONAL,
    allApplications  [600] EXPLICIT NULL OPTIONAL,
    applicationId  [601] EXPLICIT OCTET_STRING OPTIONAL,
    creationDateTime  [701] EXPLICIT INTEGER OPTIONAL,
    origin  [702] EXPLICIT INTEGER OPTIONAL,
    rollbackResistant  [703] EXPLICIT NULL OPTIONAL,
    rootOfTrust  [704] EXPLICIT RootOfTrust OPTIONAL,
    osVersion  [705] EXPLICIT INTEGER OPTIONAL,
    osPatchLevel  [706] EXPLICIT INTEGER OPTIONAL,
    attestationChallenge  [708] EXPLICIT INTEGER OPTIONAL,
    attestationApplicationId  [709] EXPLICIT OCTET_STRING OPTIONAL,
}

RootOfTrust ::= SEQUENCE {
    verifiedBootKey  OCTET_STRING,
    deviceLocked  BOOLEAN,
    verifiedBootState  VerifiedBootState,
}

VerifiedBootState ::= ENUMERATED {
    Verified  (0),
    SelfSigned  (1),
    Unverified  (2),
    Failed  (3),
}

下面的列表给出了各元素在图式:

keydescription

这个序列的值提供了密钥对被 通过密钥认证验证的一般信息和提供容易获得额外的 细节。

attestationversion
密钥的认证功能的版本。应设置为1。
attestationsecurity

这个安全 水平该认证。

笔记虽然它可能证明是 密钥存储在Android系统,如果attestationsecurity值设置为软件你 不能信任这些证明如果Android系统成为损害。

keymasterversion
大师的硬件抽象层(HAL)版本。使用0 代表0.2版或0.3版,1代表1,2代表 版本2。
keymastersecurity
这个 安全 水平实施的大师。
attestationchallenge
一个密钥对,利用关键 认证验证有关的挑战字符串。
保留
只有系统的应用程序使用该值。在所有其他应用程序,这个值是空的。
softwareenforced
可选的。keymaster the 授权 列表这是由Android系统的执行,不是由装置的通。
teeenforced
可选的。keymaster the 授权 列表这是由设备的TEE执行。

安全级别

这个数据结构在多大程度上反映了软件的功能,如 密钥对,是基于它的位置在保护装置。

因为数据结构是一个枚举,它具有完全相同的一个 以下值:

软件
创建和管理功能是在 Android系统实现逻辑。用于创建和存储密钥对的目的,这 位置比发球不太安全但比你的应用程序的 进程空间更安全。
trustedenvironment
逻辑用于创建和管理的特点是实施安全 硬件,如T恤。用于创建和存储密钥的目的, 这个位置更安全,因为安全的硬件抗 远程高度。

authorizationlist

这个数据结构包含密钥对的性质本身,在大师的硬件抽象层(HAL)的定义 。你比较这些值 到设备的当前状态或一组预期值进行验证,使用你的应用程序的一 密钥仍然有效。

每个域名对应一个类似命名的大师标签。例如, 密钥大小在授权列表字段对应的km_tag_key_size大师的标签。

在下面的列表中的每个字段是可选的:

目的
对应于 km_tag_purpose大师的标签,它使用1个标签ID值。
算法

对应于km_tag_algorithm大师的标签,它使用的 2标签的ID值。

当一个authorizationlist对象是关键 认证有关,该值始终是km_algorithm_rsa_公里_ EC算法

密钥大小
对应于 km_tag_key_size大师的标签,它使用3个标签ID值。
消化
对应于 一天_公里_文摘大师的标签,它使用5个标签ID值。
衬垫
对应于 km_tag_padding大师的标签,它使用6个标签ID值。
eccurve

对应于_公里_ EC标记_曲线大师的标签,它使用一个10 标签ID值。

用于生成椭圆曲线的参数集(EC)的密钥对, 采用ECDSA签名与验证,在Android系统 。

rsapublicexponent
对应于 _ _公里_ RSA公共_指数标签大师的标签,它使用一个标签ID 值200。
activedatetime
对应于 _ Active DateTime _公里_标签大师的标签,它使用一个标签ID值 400。
originationexpiredatetime
对应于 一天_公里_起源_ datetime _ expire大师的标签,它使用一个 标签ID值为401。
usageexpiredatetime
对应于 km_tag_usage_expire_datetime大师的标签,它使用一个标签ID 值402。
noauthrequired

对应于公里_标签_没有_ auth _ required大师的标签,它使用一个标签ID 值503。

当一个authorizationlist对象是关键 认证有关,该值始终是真实的。

userauthtype
对应于 _标签使用_ auth公里_ _类型大师的标签,它使用一个标签ID值 504。
authtimeout
对应于 公里_标签_ auth _暂停。大师的标签,它使用的 505标签的ID值。
allowwhileonbody

对应于km_tag_allow_while_on_body大师的标签, 使用价值的标签ID。

允许使用的身份验证超时时间,如果 用户还穿着他们身上的装置后的关键。注意安全 身体上的传感器确定设备是否被佩戴在用户的 体。

当一个authorizationlist对象是关键 认证有关,该值始终是真实的。

所有应用

对应于_标签应用全_ _公里大师的标签, 使用价值的标签ID。

指示是否在设备上的所有应用程序都可以访问密钥对。

当一个authorizationlist对象是关键 认证有关,该值始终是真实的。

applicationid
对应于 km_tag_application_id大师的标签,它使用一个标签ID值 601。
creationdatetime
对应于 一天_公里_ _创作的DateTime大师的标签,它使用一个标签ID 值701。
起源

对应于km_tag_origin大师的标签,它使用702个标签ID值。

当一个authorizationlist对象是关键 认证有关,这个值通常设置为km_origin_generated。如果认证使用大师 版本0.2或0.3,然而,起源可设置为km_origin_unknown相反。

rollbackresistant
对应于 km_tag_rollback_resistant大师的标签,它使用一个标签ID 值703。
rootoftrust

对应于km_tag_root_of_trust大师的标签,它使用一个标签ID值 704。

更多详情,请参见描述rootoftrust数据结构。

osversion

对应于我们把_公里_ _版本大师的标签,这 使用705标签ID值。

与 大师相关的Android操作系统的版本,指定为一六位整数。例如,版本6.0.1 表示为060001。

只有大师1或更高版本包含此值在 授权清单。

ospatchlevel

对应于km_tag_patchlevel大师的标签,这 使用706标签ID值。

与安全补丁,在大师用 相关的年份和月份,指定为一六位整数。例如,六月的 2016补丁是表示为201606。

只有大师1或更高版本包含此值在 授权清单。

attestationchallenge

对应于km_tag_attestation_challenge大师 标签,它使用一个708标签的ID值。

那是在 大师定义的密钥相关的挑战字符串。

attestationapplicationid

对应于km_tag_attestation_application_id大师的标签,它使用709个标签ID值。

那个签名密钥对 是在大师认证证书的唯一ID。

rootoftrust

这个值的集合定义了设备的状态信息。

需要的是下面列表中的每个字段:

verifiedbootkey

一个安全的关键,验证了系统的图像哈希。建议 你使用SHA-256算法对这个散列。

devicelocked
如果设备的Bootloader锁定真实,使验证启动 检查和防止符号装置的图像被闪到 装置。有关此功能的更多信息,参见 验证 启动文档
verifiedbootstate
这个 引导 状态该装置根据验证启动特征。
osversion
设备上的Android操作系统的新版本, 指定为一六位数。例如,版本6.0.1表示 060001。
patchmonthyear
与安全补丁,目前 设备上安装相关的年份和月份,指定为一六位整数。例如,六月的 2016补丁是表示为201606。

verifiedbootstate

这个数据结构提供了设备当前的启动状态,这 代表提供给用户和应用程序的 装置启动完后保护水平。有关此功能的更多信息,参见开机状态在验证启动文件部分。

这是数据结构中的枚举,因此需要准确的 以下值之一:

已验证

表示一个完整的信任链,包括Bootloader、引导 分区,所有分区和验证。

当设备在启动状态下,其verifiedbootkey是 设备的嵌入式证书的哈希,该设备制造商 添加到设备的ROM在工厂。

selfsigned

表示设备嵌入式证书验证了装置的 引导分区,签名是有效的。

当设备在启动状态下,其verifiedbootkey是的 用户安装证书的哈希,这标志一个引导分区 ,用户增加了在原来的地方的装置, 制造商提供引导。

未经证实的
指示用户可以自由修改器。因此,用户 负责检验设备的完整性。
失败

表明该装置已验证失败。认证 证书不应使用该值为verifiedbootstate



6.网络安全配置


Android N 包含一个网络安全配置特性,让应用可以在一个安全的声明性配置文件中自定义其网络安全设置,而无需修改应用代码。 可以针对特定域和特定应用配置这些设置。 该特性的主要功能如下所示:

  • 自定义信任锚:针对应用的安全连接自定义哪些证书颁发机构 (CA) 值得信赖。 例如,信任特定的自签署证书或限制应用信任的公共 CA 集。
  • 仅调试重写:在应用中以安全方式调试安全连接,而不会增加安装基数的风险。
  • Cleartext traffic 选择退出:防止应用意外使用 cleartext traffic。
  • 证书固定:将应用的安全连接限制为特定的证书。

添加安全配置文件


网络安全配置特性使用一个 XML 文件,您可以在该文件中指定应用的设置。 您必须在应用的清单中包含一个条目来指向该文件。 以下代码摘自一份清单,演示了如何创建此条目:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
  <application ... >
    <meta-data android:name="android.security.net.config"
               android:resource="@xml/network_security_config" />
    ...
  </application>
</manifest>
  
  

自定义信任的 CA


应用可能需要信任自定义的 CA 集,而不是平台默认值。 出现此情况的最常见原因包括:

  • 连接到具有自定义证书颁发机构(自签署、由公司内部 CA 签发等)的主机。
  • 将 CA 集仅限于您信任的 CA,而不是每个预装 CA。
  • 信任系统中未包含的附加 CA。

默认情况下,来自所有应用的安全(例如 TLS、HTTPS)连接均信任预装的系统 CA,而面向 API 级别 23 (Android M) 及更低级别的应用默认情况下还会信任用户添加的 CA 存储。 应用可以使用 base-config(针对应用范围的定制)或 domain-config(针对每个域的定制)自定义自己的连接。

配置自定义 CA

假设您要连接到使用自签署 SSL 证书的主机,或者连接到其 SSL 证书是由您信任的非公共 CA(如公司内部 CA)签发的主机。

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
    </domain-config>
</network-security-config>
  
  

以 PEM 或 DER 格式将自签署或非公共 CA 证书添加到 res/raw/my_ca

限制信任的 CA 集

如果应用不想信任系统信任的所有 CA,则可以自行指定,缩减要信任的 CA 集。 这样可防止应用信任任何其他 CA 签发的欺诈性证书。

限制信任的 CA 集的配置与针对特定域信任自定义 CA 相似,不同的是,前者要在资源中提供多个 CA。

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">secure.example.com</domain>
        <domain includeSubdomains="true">cdn.example.com</domain>
        <trust-anchors>
            <certificates src="@raw/trusted_roots"/>
        </trust-anchors>
    </domain-config>
</network-security-config>
  
  

以 PEM 或 DER 格式将信任的 CA 添加到 res/raw/trusted_roots。 请注意,如果使用 PEM 格式,文件必须仅包含 PEM 数据,且没有额外的文本。 您还可以提供多个 <certificates> 元素,而不是只能提供一个元素。

信任附加 CA

应用可能需要信任系统不信任的附加 CA,出现此情况的原因可能是系统还未包含此 CA,或 CA 不符合添加到 Android 系统中的要求。 应用可以通过为一个配置指定多个证书源来实现此目的。

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/extracas"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>
  
  

配置用于调试的 CA


调试通过 HTTPS 连接的应用时,您可能需要连接到没有用于生产服务器的 SSL 证书的本地开发服务器。 为了支持此操作,而又不对应用的代码进行任何修改,您可以通过使用 debug-overrides 指定仅在 android:debuggable 为 true 时才信任的仅调试 CA。 通常,IDE 和构建工具会自动为非发布版本设置此标志。

这比一般的条件代码更安全,因为出于安全考虑,应用存储不接受被标记为可调试的应用。

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cas"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>
  
  

选择退出 Cleartext Traffic


旨在连接到仅使用安全连接的目标的应用可以选择不再对这些目标提供 cleartext(使用解密的 HTTP 协议而非 HTTPS)支持。 此选项有助于防止应用因外部源(如后端服务器)提供的 URL 发生变化而意外回归。 请参阅 NetworkSecurityPolicy.isCleartextTrafficPermitted() 了解更多详情。

例如,应用可能需要确保所有与 secure.example.com 的连接始终是通过 HTTPS 完成,以防止来自恶意网络的敏感流量。

res/xml/network_security_config.xml:

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

固定证书


一般情况下,应用信任所有预装 CA。如果有预装 CA 要签发欺诈性证书,则应用将面临被中间人攻击 (MiTM) 的风险。 有些应用通过限制信任的 CA 集或通过证书固定来选择限制其接受的证书集。

通过按公钥的哈希值(X.509 证书的 SubjectPublicKeyInfo)提供证书集完成证书固定。 然后,证书链仅在至少包含一个已固定的公钥时才有效。

请注意,使用证书固定时,您应始终包含一个备份密钥,这样,当您需要强制切换到新密钥时,或更改 CA 时(固定到某个 CA 证书或该 CA 的中间证书时),您应用的连接性不会受到影响。 否则,您必须推送应用的更新以恢复连接性。

此外,可以设置固定到期时间,在该时间之后不执行证书固定。 这有助于防止尚未更新的应用出现连接问题。 不过,设置固定到期时间可能会绕过证书固定。

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>
  
  

配置继承行为


继承未在特定配置中设置的值。此行为允许进行更复杂的配置,同时保证配置文件可读。

如果未在特定条目中设置值,则使用来自下一个更通用的条目中的值。 未在 domain-config 中设置的值从父级 domain-config(如果已嵌套)或从 base-config(如果未嵌套)中获取。 未在 base-config 中设置的值使用平台默认值。

例如,考虑所有与 example.com 的子域的连接必须使用自定义 CA 集。此外,允许使用这些域的 cleartext traffic,连接到 secure.example.com 时除外。通过在 example.com 的配置中嵌套 secure.example.com 的配置,不需要重复 trust-anchors

res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="false">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>
  
  

配置文件格式


网络安全配置特性使用 XML 文件格式。 文件的整体结构如以下代码示例所示:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </base-config>

    <domain-config>
        <domain>android.com</domain>
        ...
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
        <pin-set>
            <pin digest="...">...</pin>
            ...
        </pin-set>
    </domain-config>
    ...
    <debug-overrides>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </debug-overrides>
</network-security-config>
  
  

以下部分介绍语法和文件格式的其他详细信息。

<network-security-config>

可包含:
0 或 1 个  <base-config>
任意数量的  <domain-config>
0 或 1 个  <debug-overrides>

<base-config>

语法:
<base-config usesCleartextTraffic=["true" | "false"]>
    ...
</base-config>
  
  
可包含:
<trust-anchors>
说明:
目标不在  domain-config 涵盖范围内的所有连接所使用的默认配置。

未设置的任何值均使用平台默认值。面向上述 API 级别 24 及更高级别的应用的默认配置:

<base-config usesCleartextTraffic="true">
    <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
</base-config>
    
    
面向 API 级别 23 及更低级别的应用的默认配置:
<base-config usesCleartextTraffic="true">
    <trust-anchors>
        <certificates src="system" />
        <certificates src="user" />
    </trust-anchors>
</base-config>
    
    

<domain-config>

语法:
<domain-config usesCleartextTraffic=["true" | "false"]>
    ...
</domain-config>
    
    
可包含:
1 个或多个  <domain> 
0 或 1 个  <trust-anchors> 
0 或 1 个  <pin-set> 
任意数量的已嵌套  <domain-config>
说明
用于按照  domain 元素的定义连接到特定目标的配置。

请注意,如果有多个 domain-config 元素涵盖某个目标,则使用匹配域规则最具体(最长)的配置。

<domain>

语法:
<domain includeSubdomains=["true" | "false"]>example.com</domain>
    
    
属性:
includeSubdomains
如果为  "true",则此域规则与域及所有子域(包括子域的子域)匹配,否则,该规则仅适用于精确匹配项。
说明:

<debug-overrides>

语法:
<debug-overrides>
    ...
</debug-overrides>
    
    
可包含:
0 或 1 个  <trust-anchors>
说明:
当  android:debuggable 为  "true" 时将应用的重写,IDE 和构建工具生成的非发布版本通常属于此情况。 将在  debug-overrides 中指定的信任锚添加到所有其他配置,并且当服务器的证书链使用其中一个仅调试信任锚时不执行证书固定。 如果  android:debuggable 为  "false",则完全忽略此部分。

<trust-anchors>

语法:
<trust-anchors>
...
</trust-anchors>
    
    
可包含:
任意数量的  <certificates>
说明:
用于安全连接的信任锚集

<certificates>

语法:
<certificates src=["system" | "user" | "raw resource"]
              overridePins=["true" | "false"] />
    
    
说明:
用于  trust-anchors 元素的 X.509 证书集。
属性:
src
CA 证书的来源,可以是
  • 指向包含 X.509 证书的文件的原始资源 id。 证书必须以 DER 或 PEM 格式编码。如果为 PEM 证书,则文件不得包含额外的非 PEM 数据,如注释。
  • 用于预装系统 CA 证书的 "system"
  • 用于用户添加的 CA 证书的 "user"
overridePins

指定来自此源的 CA 是否绕过证书固定。如果为 "true",则为穿过此源的其中一个 CA 的链颁发证书,并且不执行证书固定。 这对于调试 CA 或支持用户对应用的安全流量进行中间人攻击 (MiTM) 非常有用。

默认值为 "false",除非在 debug-overrides 元素中另外指定(在这种情况下,默认值为 "true")。

<pin-set>

语法:
<pin-set expiration="date">
...
</pin-set>
      
      
可包含:
任意数量的  <pin>
说明:
公钥固定 (PKP) 集。对于要信任的安全连接,信任链中必须有一个公钥位于 PKP 集中。 有关固定形式,请参阅  <pin>
属性:
expiration
采用  yyyy-MM-dd 格式的日期,在该日期及之后固定过期,因而禁用固定。 如果未设置该属性,则固定不会过期。

设置到期时间有助于防止未更新到其 PKP 集(例如,由于用户禁用应用更新)的应用出现连接问题。

<pin>

语法:
<pin digest=["SHA-256"]>base64 encoded digest of X.509
    SubjectPublicKeyInfo (SPKI)</pin>
      
      
属性:
digest

用于生成 PKP 的摘要算法。目前仅支持 "SHA-256"



7.ICU4J Android


ICU4J 是一个广泛使用的开源 Java 库集合,为软件应用提供 Unicode 和全球化支持。 Android N 在 android.icu 软件包下显示 Android 框架中的 ICU4J API 子集,供应用开发者使用。 这些 API 使用设备上具有的本地化数据。 因此,您可以通过不将 ICU4J 库编译到 APK 来减少 APK 占用空间;相反,您可以只在框架中调用它们。 (在此情况下,您可能想要提供多个版本的 APK,这样,运行比 Android N 低的 Android 版本的用户可以下载包含 ICU4J 库的应用版本。)

本文档开头提供了有关支持这些库所需的最低 Android API 级别的一些基本信息。 然后,介绍关于 Android 特定的 ICU4J 实现您需要了解的内容。 最后,介绍如何在 Android 框架中使用 ICU4J API。

与 ICU4J 的关系


Android N 通过 android.icu 软件包(而非 com.ibm.icu)显示 ICU4J API 的子集。由于种种原因,Android 框架可能选择不显示 ICU4J API;例如,Android N 不显示一些已弃用的 API 或 ICU 团队尚未将其声明为“稳定”的 API。 由于 ICU 团队将来会弃用这些 API,因此,Android 也会将其标记为已弃用,但将继续包含它们。

表 1. Android N 中使用的 ICU 和 CLDR 版本。

Android API 级别 ICU 版本 CLDR 版本
Android N 56 28

以下是几点注意事项:

  • ICU4J Android 框架 API 不包含所有的 ICU4J API。
  • NDK 开发者应了解 Android ICU4C 不受支持。
  • Android 框架中的 API 不会取代 Android 对使用资源进行本地化的支持。

从 com.ibm.icu 迁移至 android.icu 软件包


如果您已在应用中使用 ICU4J API,且 android.icu API 符合您的要求,那么要迁移至框架 API,需要将 Java 导入从 com.ibm.icu 更改为 android.icu。 然后,您可以从 APK 移除您自己的 ICU4J 文件的副本。

:ICU4J 框架 API 使用 android.icu 命名空间,而不是 com.ibm.icu。这是为了避免在包含自己的 com.ibm.icu 库的 APK 中出现命名空间冲突。

从其他 Android SDK API 迁移至 android.icu API

java 和 android 软件包中的某些类与在 ICU4J 中找到的一些类等效。 不过,ICU4J 通常为标准和语言提供更广泛的支持。

下面是一些入门示例:

替代项
java.lang.Character android.icu.lang.UCharacter
java.text.BreakIterator android.icu.text.BreakIterator
java.text.DecimalFormat android.icu.text.DecimalFormat
java.util.Calendar android.icu.util.Calendar
android.text.BidiFormatter android.icu.text.Bidi
android.text.format.DateFormat android.icu.text.DateFormat
android.text.format.DateUtils android.icu.text.DateFormat android.icu.text.RelativeDateTimeFormatter

授权


ICU4J 按照 ICU 许可发布。如需了解详情,请参阅 ICU 用户指南


8.Java 8 支持


Android N 支持 Java 8 语言功能,您可以在开发面向 Android N 的应用时使用这些功能。本页面介绍了 Android N Preview 中支持的新语言功能、如何正确设置项目以使用这些功能,以及您可能遇到的任何已知问题。

要开始使用这些功能,您需要下载并设置 Android Studio 2.1 和 Android N Preview SDK,包括所需的 Jack 工具链和更新的 Android Plugin for Gradle。 如果您尚未安装 Android N Preview SDK,请参阅 Develop for Android N 设置

注:开发面向 Android N 平台的应用并不要求必须使用新的 Java 8 语言功能。 如果您不想使用 Java 8 语言功能编写代码,您可以将项目的源和目标兼容性值保留为 Java 7,但您仍必须使用 JDK 8 进行编译,以便针对 Android N 平台进行构建。

支持的 Java 8 语言功能和 API


Android 目前仅支持部分 Java 8 语言功能。在开发面向 Android N Preview 的应用时,现已可使用以下功能:

注:在 Android 的较早版本中测试 Lambda 表达式和方法引用,前往您的 build.gradle 文件,将 compileSdkVersion 和 targetSdkVersion 设置为 23 或更低版本。 您仍需要启用 Jack 工具链以使用这些 Java 8 功能。

此外,现已可使用以下 Java 8 语言功能 API:

启用 Java 8 功能和 Jack 工具链


要使用新的 Java 8 语言功能,还需使用新的 Jack 工具链。新的 Android 工具链将 Java 源语言编译成 Android 可读取的 Dalvik 可执行文件字节码,且有其自己的 .jack 库格式,在一个工具中提供了大多数工具链功能:重新打包、压缩、模糊化以及 Dalvik 可执行文件分包。

以下是构建 Android Dalvik 可执行文件可用的两种工具链的对比:

  • 旧版 javac 工具链:
    javac (.java --> .class) --> dx (.class --> .dex)
  • 新版 Jack 工具链:
    Jack (.java --> .jack --> .dex)

配置 Gradle

如需为您的项目启用 Java 8 语言功能和 Jack,请在模块层级的 build.gradle 文件中输入以下内容:

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}
  
  

已知问题

Instant Run 目前不能用于 Jack,在使用新的工具链时将被禁用。

由于 Jack 在编译应用时不生成中间类文件,依赖这些文件的工具目前不能用于 Jack。例如以下工具:

  • 对类文件进行操作的 Lint 检测工具
  • 需要应用类文件的工具和库(例如使用 JaCoCo 进行仪器测试中)

如果您在使用 Jack 的过程中发现其他问题,请提交错误



9.Android for Work 更新


QR 码配置


Android for Work 现在支持使用 QR 码来配置企业负责的设备。 安装向导现在允许您通过扫描 QR 码来配置设备。

工作资料安全性挑战


个人资料所有者可以要求用户为在工作资料中运行的应用指定安全性挑战。 系统会在用户尝试打开任何工作应用时显示安全性挑战。 如果用户成功完成安全性挑战,系统可解锁工作资料并将其解密(如果需要)。

如果个人资料所有者发送一项 ACTION_SET_NEW_PASSWORD Intent,系统会提示用户设置一项安全性挑战。 个人资料所有者也可以发送一项 ACTION_SET_NEW_PARENT_PROFILE_PASSWORD Intent 来提示用户设置设备锁定。

个人资料所有者可以选择为工作挑战设置不同于其他设备密码策略的密码策略。 例如,设备挑战响应的最小长度可以与其它密码要求的长度不同。 个人资料所有者使用常见的 DevicePolicyManager 方法来设置挑战策略,如 setPasswordQuality() 和 setPasswordMinimumLength()。 个人资料所有者还能通过使用由新的DevicePolicyManager.getParentProfileInstance() 方法返回的 DevicePolicyManager 实例来设置设备锁定, 此外,个人资料所有者可以使用 DevicePolicyManager 类的新 setOrganizationColor() 和setOrganizationName() 方法来自定义工作挑战的凭据屏幕。

有关新方法和常量的详细信息,请参阅 N Preview SDK 参考 中的 DevicePolicyManager 参考页面。

停用应用访问


设备所有者和个人资料所有者可以通过调用新 DevicePolicyManager.setPackagesSuspended() 方法来临时暂停软件包访问。 所有者可以用同样的方法来重新启用这些软件包。

在软件包被暂停期间,它不能启动 Activity ,到软件包的通知会被阻止,而概览屏幕中的应用条目会被隐藏。 暂停的软件包不会显示在概览屏幕中,并且它们不能显示对话(包括提醒和 Snackbar), 也不能播放音频或振动设备。

启动器应对暂停的应用应用一个独特的 UI,以指示应用目前不可用;例如它们可以将应用图标显示为灰色。 启动器可以通过调用新DevicePolicyManager.getPackageSuspended() 方法来确定应用是否被暂停。

切换工作模式


在双个人资料设备上,用户可以打开或关闭工作模式。在工作模式关闭时,托管个人资料会被暂时关闭。 工作资料应用、后台同步和通知都会被停用,包括个人资料所有者应用。 在工作资料被停用时,系统会显示一个持久的状态图标,以提醒用户他们将无法启动工作应用。 系统启动器会指示工作应用和小工具都将无法访问。

Always-On VPN


设备所有者和个人资料所有者可以要求工作应用始终通过指定的 VPN 连接到网络。 如果所有者设置了此要求,设备会在启动时自动开始 VPN。

所有者可以通过调用新 DevicePolicyManager.setAlwaysOnVpnPackage() 方法来要求使用 VPN。 通过调用新DevicePolicyManager.GetAlwaysOnVpnPackage() 方法来确定所有者是否设置了 VPN 要求。

由于 VPN 服务无需应用交互即可由系统直接绑定,因此,VPN 客户端必须针对 Always on VPN 处理新的入口点。 像以前一样,您可以通过使用与操作android.NET.VpnService 匹配的 Intent 过滤器查找活动的服务。

用户可以使用 Settings > More > VPN 屏幕来手动设置实现 VpnService 的 Always on VPN 客户端。

联系人与工作资料集成


个人资料所有者可以允许主用户对工作联系人进行本地搜索和目录查找。 例如,用户可以从他们的个人拨号器或联系人应用访问个人和工作目录联系人(如果他们的个人资料管理员允许)。

利用联系人提供程序的开发者可以使用企业联系人 API 从主用户访问工作资料目录条目(如果策略允许):

  • ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
  • ContactsContract.Phone.ENTERPRISE_CONTENT_FILTER_URI
  • ContactsContract.Email.ENTERPRISE_CONTENT_FILTER_URI
  • ContactsContract.Callable.ENTERPRISE_CONTENT_FILTER_URI
  • ContactsContract.Directory.ENTERPRISE_CONTENT_URI
  • ContactsContract.Directory.isEntepriseDirectoryId()

个人资料所有者可以使用以下新方法来控制主用户的工作联系人可见性:

  • DevicePolicyManager.setCrossProfileContactsSearchDisabled()
  • DevicePolicyManager.getCrossProfileContactsSearchDisabled()

远程重新启动


设备所有者可以远程重新启动设备。有些情况下,部署在公共场所的封装内设备会阻止访问电源按钮。 如果设备需要重新启动,管理员可以使用新DevicePolicyManager.reboot() 方法来进行此操作。

停用数据网络漫游


设备所有者可以使用新的 UserManager 用户限制 DISALLOW_DATA_ROAMING 来停用数据网络漫游。

企业进程记录


设备所有者可以通过远程跟踪设备活动来识别可疑活动 ,包括应用启动、ADB Activity 和屏幕解锁。 进程记录不要求用户同意。 若要检索记录,设备所有者可以使用 DevicePolicyManager.setSecurityLoggingEnabled() 来启用设备记录。

API 更改包括:

  • 新类 android.app.admin.SecurityLog 和它的方法
  • void DevicePolicyManager.setSecurityLoggingEnabled()
  • boolean DevicePolicyManager.isSecurityLoggingEnabled()
  • List<SecurityEvent> DevicePolicyManager.retrieveSecurityLogs()
  • List<SecurityEvent> DevicePolicyManager.retrievePreRebootSecurityLogs()
  • void DeviceAdminReceiver.onSecurityLogsAvailable()

远程错误报告


设备所有者可以远程触发和检索包含设备状态转储文件的错误报告,这允许对已知事故或受损害的设备进行取证调查。 鉴于错误报告的详细性质,需要经过用户同意。

Android N 包括以下 API 添加来支持此功能。如需详细信息,请参阅 N Preview SDK 参考

  • DevicePolicyManager.requestBugreport()
  • DeviceAdminReceiver.onBugreportFailed()
  • DeviceAdminReceiver.onBugreportShared()
  • DeviceAdminReceiver.onBugreportSharingDeclined()
  • DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING
  • DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE

移除客户端证书


个人资料所有者和设备所有者现在可以通过调用新方法 DevicePolicyManager.removeKeyPair() 来移除通过 installKeyPair() 安装的客户端证书。

允许访问安装设备上的客户端证书


如果个人资料所有者或设备所有者授予第三方应用管理证书的权限,应用便可授予自身访问其所安装证书的权限,无需所有者进行任何干预。

用于管理证书的现有 API 经过扩展,现包括:

  • DevicePolicyManager.installKeyPair()

系统 UI 策略透明度


影响用户体验或限制用户设置的策略是向用户完全公开的,个人资料所有者和设备所有者可以将策略归于他们公司的 IT 部门。 除了“Settings”中一贯的“Action not allowed”消息之外,IT 管理员可以通过以下新 DevicePolicyManager 方法在设备设置中设置组织特定的支持消息:

  • DevicePolicyManager.setShortSupportMessage()
  • DevicePolicyManager.setLongSupportMessage()

应用限制管理增强


设备或个人资料所有者可以通过新 DevicePolicyManager.setApplicationRestrictionsManagingPackage() 方法启用另一项应用,以管理应用限制。 提名的应用可以通过调用 DevicePolicyManager.isCallerApplicationRestrictionsManagingPackage() 来检查是否已被授予此权限。

提名管理应用限制的应用可以为该用户或个人资料内的任何软件包调用 setApplicationRestrictions() 和 getApplicationRestrictions()

位置关闭开关


用户可以停用工作应用的位置权限,同时仍可继续在个人应用中访问位置信息。 Location Settings 中的一个单独的位置访问开关允许用户拒绝对工作资料内运行的应用的位置更新或最后位置查询。

顶层位置关闭开关停用对主个人资料和托管个人资料的位置访问权限。

自定义配置


应用可以用企业颜色和徽标来自定义个人资料所有者和设备所有者配置流程。

DevicePolicyManager.EXTRA_PROVISIONING_MAIN_COLOR
自定义流程颜色。
DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI
用企业徽标来自定义流程。

多 Wi-Fi CA 证书


个人资料所有者和设备所有者可以为一项指定的 Wi-Fi 配置设置多个 CA 证书。 如果企业 Wi-Fi 网络针对有相同 SSID 的独立接入点设置了独立 CA,IT 管理员可以使用新 setCaCertificates() 方法将所有相关 CA 包括在 Wi-Fi 配置中。

添加的 API 有:

  • WifiEnterpriseConfig.setCaCertificates()
  • WifiEnterpriseConfig.getCaCertificates()

自定义锁屏消息


设备所有者可以提供将显示在锁屏上的所有者信息。 该信息的优先级高于用户锁屏消息(如果已设置)。 新 DevicePolicyManager 方法有:

  • setDeviceOwnerLockScreenInfo()
  • getDeviceOwnerLockScreenInfo()

工作资料 ConnectionService


个人资料所有者可以为调用后端(调用帐户)指定一项使用工作特定 ConnectionService 的工作拨号器应用。 工作拨号器会保持仅限工作的调用记录,并仅依赖于工作联系人。 无论拨号应用是什么,用户都将获得一致的调用内 UI 体验。 传入到工作调用帐户的工作调用会与传入到个人调用帐户的个人调用相区别。

拨号器应检查新标记 android.telecom.Call.PROPERTY_WORK_CALL,以确定一项调用是否是工作调用。 如果一项调用是工作调用,拨号器应通过添加工作徽章等方式做如此指示。

锁定壁纸


新的用户限制 (DISALLOW_SET_WALLPAPER) 可以阻止用户更改他们的壁纸。 设备所有者或个人资料所有者仍可以改变壁纸,但只能更改他们控制的用户或个人资料的壁纸。 例如,个人资料所有者不能更改父用户的壁纸,但主个人资料的个人资料所有者或设备所有者却可以。 想要更改壁纸的个人资料所有者或设备所有者应检查他们管理的用户或个人资料是否有壁纸 (isWallpaperSupported()) 以及他们是否被允许更改这个壁纸(通过新方法WallpaperManager.isWallpaperSettingAllowed())。

锁定用户图标


新的用户限制 (DISALLOW_SET_USER_ICON) 阻止用户更改其用户图标。 用户的设备所有者或个人资料所有者仍可以更改图标。 但是个人资料所有者只能更改其控制的个人资料的用户图标。

设备运行状况监测


设备所有者或个人资料所有者可以使用新的 HardwarePropertiesManager 界面来检索有关设备运行状况的信息,如 CPU 或 GPU 温度和 CPU 使用率。 新的监测界面特别适合监测在远程位置运行的无人值守设备。


10.作用域目录访问


应用(如照片应用)通常只需要访问外部存储中的特定目录,例如 Pictures 目录。 现有的外部存储访问方法未经专门设计,无法轻松地为这些类型的应用提供目标目录访问。 例如:

  • 在您的清单中请求 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 将允许访问外部存储上的所有公共目录,这可能导致访问的内容超出应用需要的内容。
  • 使用存储访问框架通常会让您的用户通过一个系统 UI 选取目录,如果应用始终访问同一个外部目录,则该操作没有任何必要。

Android N 提供简化的全新 API 以访问通用外部存储目录。

访问外部存储目录


使用 StorageManager 类获取适当的 StorageVolume 实例。然后,通过调用该实例的 StorageVolume.createAccessIntent() 方法创建一个 Intent。使用此 Intent 访问外部存储目录。 若要获取所有可用卷的列表,包括可移动介质卷,请使用 StorageManager.getVolumesList()

如果您有关于特定文件的信息,使用 StorageManager.getStorageVolume(File) 来获得包含该文件的 StorageVolume。 调用在 StorageVolume 上的createAccessIntent() 以访问文件的外部存储目录。

在二级卷(例如外部 SD 卡)上,当调用 StorageVolume.createAccessIntent() 以请求访问整个卷,而不是特定目录时,传入“null”。如果您向主要卷传入“null”,或者如果您传入无效的目录名,StorageVolume.createAccessIntent() 将返回“null”。

以下代码段展示如何在主要共享存储中打开Pictures 目录:

StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
StorageVolume volume = sm.getPrimaryVolume();
Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);
  
  

系统尝试授予对外部目录的访问权限,并使用一个简化的 UI 向用户确认访问权限(如果需要):


图 1. 一个请求访问 Pictures 目录的应用。

如果用户授予访问权限,则系统会调用 onActivityResult() 重写方法,且结果代码为 Activity.RESULT_OK,Intent 数据包含 URI。使用提供的 URI 访问目录信息,与使用存储访问框架返回的 URI 类似。

如果用户不授予访问权限,则系统会调用 onActivityResult() 重写方法,且结果代码为 Activity.RESULT_CANCELED,Intent 数据为 null。

:获得特定外部目录的访问权限也会获得该目录中子目录的访问权限。

访问可移动介质上的目录


若要使用作用域目录访问来访问可移动介质上的目录,首先要添加一个用于侦听 MEDIA_MOUNTED 通知的 BroadcastReceiver,例如:

<receiver
    android:name=".MediaMountedReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>
  
  

当用户装载可移动介质时,如 SD 卡,系统将发送一则 MEDIA_MOUNTED 通知。此通知在 Intent 数据中提供一个 StorageVolume 对象,您可用它访问可移动介质上的目录。 以下示例访问可移动介质上的 Pictures 目录:

// BroadcastReceiver has already cached the MEDIA_MOUNTED
// notification Intent in mediaMountedIntent
StorageVolume volume = (StorageVolume)
    mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);
  
  

最佳做法


请尽可能保留外部目录访问 URI,这样即不必重复要求用户授予访问权限。 在用户授予访问权限后,使用目录访问 URI 调用getContentResolver().takePersistableUriPermssion()。 系统将保留此 URI,后续的访问请求将返回 RESULT_OK,且不会向用户显示确认 UI。

如果用户拒绝授予外部目录访问权限,请勿立即再次请求访问权限。 一再不停地请求访问权限会导致非常差的用户体验。 如果用户拒绝了一项请求,而应用再次请求访问,UI 会显示一个 Don't ask again 复选框:


图 1. 应用第二次请求访问可移动介质。

如果用户选择 Don't ask again 并拒绝请求,您的应用向指定目录提出的所有未来请求都将被自动拒绝,并且将不会有请求 UI 呈现给用户。



转载请注明:  http://blog.csdn.net/wen_demo/article/details/51943340
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值