1.
分组资源类型
您应将各种资源放入项目 res/
目录的特定子目录下。例如,以下是一个简单项目的文件层次结构:
MyProject/ src/ MyActivity.java res/ drawable/ graphic.png layout/ main.xml info.xml mipmap/ icon.png values/ strings.xml
正如您在此示例中所看到的那样,res/
目录包含所有资源(在子目录下):一个图像资源、两个布局资源、启动器图标的 mipmap/
目录以及一个字符串资源文件。资源目录名称非常重要,将在表 1 中进行介绍。
注:如需了解有关使用 mipmap 文件夹的详细信息,请参阅管理项目概览。
表 1. 项目 res/
目录内支持的资源目录。
目录 | 资源类型 |
| 用于定义属性动画的 XML 文件。 |
| 定义渐变动画的 XML 文件。(属性动画也可以保存在此目录中,但是为了区分这两种类型,属性动画首选 |
| 用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源 |
| 位图文件(
请参阅 可绘制对象资源。 |
| 适用于不同启动器图标密度的可绘制对象文件。如需了解有关使用 |
| 用于定义用户界面布局的 XML 文件。 请参阅布局资源。 |
| 用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML 文件。请参阅菜单资源。 |
| 要以原始形式保存的任意文件。要使用原始 但是,如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 |
| 包含字符串、整型数和颜色等简单值的 XML 文件。 其他 由于每个资源均用其自己的 XML 元素定义,因此您可以根据自己的需要命名文件,并将不同的资源类型放在一个文件中。但是,为了清晰起见,您可能需要将独特的资源类型放在不同的文件中。 例如,对于可在此目录中创建的资源,下面给出了相应的文件名约定: |
| 可以在运行时通过调用 |
注意:切勿将资源文件直接保存在 res/
目录内,这会导致出现编译错误。
2.
Android 如何查找最佳匹配资源
当您请求要为其提供备用资源的资源时,Android 会根据当前的设备配置选择要在运行时使用的备用资源。为演示 Android 如何选择备用资源,假设以下可绘制对象目录分别包含相同图像的不同版本:
drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/
同时,假设设备配置如下:
语言区域 = en-GB
屏幕方向 = port
屏幕像素密度 = hdpi
触摸屏类型 = notouch
主要文本输入法 = 12key
通过将设备配置与可用的备用资源进行比较,Android 从 drawable-en-port
中选择可绘制对象。
系统使用以下逻辑决定要使用的资源:
图 2. Android 如何查找最佳匹配资源的流程图。
- 淘汰与设备配置冲突的资源文件。
drawable-fr-rCA/
目录与en-GB
语言区域冲突,因而被淘汰。 drawable/ drawable-en/drawable-fr-rCA/drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/ 例外:屏幕像素密度是唯一一个未因冲突而被淘汰的限定符。 尽管设备的屏幕密度为 hdpi,但是drawable-port-ldpi/
未被淘汰,因为此时每个屏幕密度均视为匹配。如需了解详细信息,请参阅支持多种屏幕文档。 - 选择列表(表 2)中(下一个)优先级最高的限定符。(先从 MCC 开始,然后下移。)
- 是否有资源目录包括此限定符?
- 若无,请返回到第 2 步,看看下一个限定符。(在该示例中,除非达到语言限定符,否则答案始终为“否”。)
- 若有,请继续执行第 4 步。
- 淘汰不含此限定符的资源目录。在该示例中,系统会淘汰所有不含语言限定符的目录。
drawable/drawable-en/ drawable-en-port/ drawable-en-notouch-12key/drawable-port-ldpi/drawable-port-notouch-12key/例外:如果涉及的限定符是屏幕像素密度,则 Android 会选择最接近设备屏幕密度的选项。通常,Android 倾向于缩小大型原始图像,而不是放大小型原始图像。请参阅支持多种屏幕。- 返回并重复第 2 步、第 3 步和第 4 步,直到只剩下一个目录为止。在此示例中,屏幕方向是下一个判断是否匹配的限定符。因此,未指定屏幕方向的资源被淘汰:
drawable-en/drawable-en-port/drawable-en-notouch-12key/剩下的目录是drawable-en-port
。
尽管对所请求的每个资源均执行此程序,但是系统仍会对某些方面做进一步优化。 例如,系统一旦知道设备配置,即会淘汰可能永远无法匹配的备用资源。 比如说,如果配置语言是英语(“en”),则系统绝不会将语言限定符设置为非英语的任何资源目录包含在选中的资源池中(不过,仍会将不带语言限定符的资源目录包含在该池中)。
根据屏幕尺寸限定符选择资源时,如果没有更好的匹配资源,则系统将使用专为小于当前屏幕的屏幕而设计的资源(例如,如有必要,大尺寸屏幕将使用标准尺寸的屏幕资源)。 但是,如果唯一可用的资源大于当前屏幕,则系统不会使用这些资源,并且如果没有其他资源与设备配置匹配,应用将会崩溃(例如,如果所有布局资源均用 xlarge
限定符标记,但设备是标准尺寸的屏幕)。
注:限定符的优先顺序(表 2 中)比与设备完全匹配的限定符数量更加重要。例如,在上面的第 4 步中,列表剩下的最后选项包括三个与设备完全匹配的限定符(方向、触摸屏类型和输入法),而 drawable-en
只有一个匹配参数(语言)。但是,语言的优先顺序高于其他两个限定符,因此 drawable-port-notouch-12key
被淘汰。
3.
处理运行时变更
https://developer.android.google.cn/guide/topics/resources/runtime-changes
4.
为此,您可以创建一个名为的小文件 res/values-en-rGB/strings.xml
,其中仅包含应用程序在英国运行时应该不同的字符串。对于所有其余字符串,应用程序将回退到默认值并使用中定义的内容res/values/strings.xml
。
5.
注:您的应用仍需要明确请求其需要的每项权限,即使用户已向应用授予该权限组中的其他权限。此外,权限分组在将来的 Android 版本中可能会发生变化。您的代码不应依赖特定权限属于或不属于相同组这种假设。
例如,假设您在应用清单中列出了 READ_CONTACTS
和 WRITE_CONTACTS
。如果您请求 READ_CONTACTS
且用户授予了此权限,那么,当您请求 WRITE_CONTACTS
时,系统将立即授予您该权限,不会与用户交互。
如果用户拒绝了某项权限请求,您的应用应采取适当的操作。例如,您的应用可能显示一个对话框,解释它为什么无法执行用户已经请求但需要该权限的操作。
当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。这种情况下,无论应用在什么时候使用 requestPermissions()
再次要求该权限,系统都会立即拒绝此请求。系统会调用您的 onRequestPermissionsResult()
回调方法,并传递 PERMISSION_DENIED
,如果用户再次明确拒绝了您的请求,系统将采用相同方式操作。这意味着当您调用 requestPermissions()
时,您不能假设已经发生与用户的任何直接交互。
6.
运行时请求(Android 6.0及更高版本)
如果设备运行的是Android 6.0(API级别23)或更高版本, 并且应用程序的数量targetSdkVersion 为23或更高,则在安装时不会通知用户任何应用程序权限。您的应用必须要求用户在运行时授予危险权限。当您的应用请求权限时,用户会看到一个系统对话框(如图1左侧所示),告诉用户您的应用尝试访问哪个权限组。该对话框包括拒绝和允许按钮。
如果用户拒绝权限请求,则下次您的应用请求权限时,该对话框包含一个复选框,选中该复选框后,表示不希望再次提示用户获得权限(参见图2,右侧)。
图1.初始权限对话框(左)和次要权限请求以及关闭进一步请求的选项(右)
如果用户选中“ 永不再询问”框并点击 “拒绝”,则系统不再提示用户以后是否尝试请求相同的权限。
即使用户授予您的应用程序所请求的权限,您也不能总是依赖它。用户还可以选择在系统设置中逐个启用和禁用权限。您应该始终在运行时检查并请求权限以防止运行时错误(SecurityException
)。
有关如何处理运行时权限请求的详细信息,请参阅 请求应用程序权限。
7.
可选硬件功能的权限
访问某些硬件功能(如蓝牙或相机)需要应用程序权限。但是,并非所有Android设备都具有这些硬件功能。因此,如果您的应用请求 CAMERA
权限,那么您还必须<uses-feature>在清单中包含 标记,以声明是否确实需要此功能。例如:
<uses-feature android:name="android.hardware.camera" android:required="false" />
如果您声明android:required="false"
该功能,则Google Play允许您的应用安装在没有此功能的设备上。然后,您必须通过调用检查当前设备是否在运行时具有该功能 PackageManager.hasSystemFeature()
,并在该功能不可用时正常禁用该功能。
如果您未提供 <uses-feature>代码,那么当Google Play看到您的应用请求相应的权限时,它会认为您的应用需要此功能。因此,它会从没有该功能的设备中过滤您的应用,就像您android:required="true"
在 <uses-feature>标记中声明一样 。
8.
保护水平
权限分为几个保护级别。保护级别会影响是否需要运行时权限请求。
有三种保护级别会影响第三方应用程序: 正常,签名和危险权限。
正常权限
普通权限涵盖了应用程序需要访问应用程序沙箱外部数据或资源的区域,但用户隐私或其他应用程序操作的风险非常小。例如,设置时区的权限是正常权限。
如果应用程序在其清单中声明它需要正常权限,则系统会在安装时自动授予应用程序该权限。系统不会提示用户授予正常权限,用户也无法撤消这些权限。
从Android 9(API级别28)开始,以下权限分类为 PROTECTION_NORMAL
:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
- FOREGROUND_SERVICE
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MANAGE_OWN_CALLS
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_COMPANION_RUN_IN_BACKGROUND
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
REQUEST_DELETE_PACKAGES
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
SET_ALARM
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
签名权限
系统会在安装时授予这些应用程序权限,但仅限于尝试使用权限的应用程序使用与定义权限的应用程序相同的证书进行签名时。
注意:某些签名权限不适用于第三方应用程序。
从Android 8.1(API级别27)开始,第三方应用程序可以使用的以下权限分类为 PROTECTION_SIGNATURE:
- BIND_ACCESSIBILITY_SERVICE
- BIND_AUTOFILL_SERVICE
- BIND_CARRIER_SERVICES
- BIND_CHOOSER_TARGET_SERVICE
- BIND_CONDITION_PROVIDER_SERVICE
- BIND_DEVICE_ADMIN
- BIND_DREAM_SERVICE
- BIND_INCALL_SERVICE
- BIND_INPUT_METHOD
- BIND_MIDI_DEVICE_SERVICE
- BIND_NFC_SERVICE
- BIND_NOTIFICATION_LISTENER_SERVICE
- BIND_PRINT_SERVICE
- BIND_SCREENING_SERVICE
- BIND_TELECOM_CONNECTION_SERVICE
- BIND_TEXT_SERVICE
- BIND_TV_INPUT
- BIND_VISUAL_VOICEMAIL_SERVICE
- BIND_VOICE_INTERACTION
- BIND_VPN_SERVICE
- BIND_VR_LISTENER_SERVICE
- BIND_WALLPAPER
- CLEAR_APP_CACHE
- MANAGE_DOCUMENTS
- READ_VOICEMAIL
- REQUEST_INSTALL_PACKAGES
- SYSTEM_ALERT_WINDOW
- WRITE_SETTINGS
- WRITE_VOICEMAIL
危险的权限
危险权限涵盖应用程序需要涉及用户私人信息的数据或资源的区域,或者可能会影响用户存储的数据或其他应用程序的操作。例如,读取用户联系人的权限是一种危险的权限。如果应用声明它需要危险权限,则用户必须明确授予该应用的权限。在用户批准该权限之前,您的应用无法提供依赖该权限的功能。
要使用危险权限,您的应用必须提示用户在运行时授予权限。有关如何提示用户的更多详细信息,请参阅 危险权限的请求提示。
有关危险权限的列表,请参阅下面的表1。
特殊权限
有一些权限不像正常和危险的权限。SYSTEM_ALERT_WINDOW
并且WRITE_SETTINGS
特别敏感,因此大多数应用程序不应使用它们。如果应用程序需要其中一个权限,则必须在清单中声明权限,并发送请求用户授权的意图。系统通过向用户显示详细的管理屏幕来响应意图。
有关如何请求这些权限的详细信息,请参阅参考SYSTEM_ALERT_WINDOW
和 WRITE_SETTINGS
参考条目。
可以在以下位置找到Android系统提供的所有权限 Manifest.permission
。
许可组
权限分组为与设备功能或功能相关的组。在此系统下,权限请求在组级别处理,单个权限组对应于应用程序清单中的多个权限声明。例如,SMS组包括声明READ_SMS
和 RECEIVE_SMS
声明。以这种方式对权限进行分组使用户能够做出更有意义和更明智的选择,而不会被复杂和技术许可请求所淹没。
所有危险的Android权限都属于权限组。无论保护级别如何,任何权限都可以属于权限组。但是,如果权限是危险的,则权限组仅影响用户体验。
如果设备运行的是Android 6.0(API级别23)且应用程序targetSdkVersion为23或更高,则当您的应用请求危险权限时,以下系统行为适用:
- 如果应用程序当前在权限组中没有任何权限,系统会向用户显示描述应用程序要访问的权限组的权限请求对话框。该对话框未描述该组中的特定权限。例如,如果某个应用请求了该
READ_CONTACTS
权限,系统对话框就会说该应用需要访问该设备的联系人。如果用户授予批准,系统将为应用程序提供其请求的权限。 - 如果应用程序已在同一权限组中被授予其他危险权限,则系统会立即授予权限,而不与用户进行任何交互。例如,如果某个应用程序先前已请求并已获得该
READ_CONTACTS
权限,然后它会请求WRITE_CONTACTS
,则系统会立即授予该权限,而不向用户显示权限对话框。
警告:未来版本的Android SDK可能会将特定权限从一个组移动到另一个组。因此,请勿将应用程序的逻辑基于这些权限组的结构。
例如,与Android 8.1(API级别27)READ_CONTACTS
属于同一权限组 WRITE_CONTACTS
。如果您的应用请求READ_CONTACTS
权限,然后请求 WRITE_CONTACTS
权限,请不要假设系统可以自动授予 WRITE_CONTACTS
权限。
如果设备运行Android 5.1(API级别22)或更低版本,或者应用程序 targetSdkVersion为22或更低,系统会要求用户在安装时授予权限。系统再次告诉用户应用程序需要哪些权限组,而不是单个权限。例如,当应用程序请求READ_CONTACTS
安装对话框时,列出“联系人”组。用户接受时,仅READ_CONTACTS
授予应用程序权限。
注意:即使用户已在同一组中授予了其他权限,您的应用仍需要明确请求其所需的每个权限。此外,将权限分组到组中可能会在将来的Android版本中发生变化。您的代码不应具有依赖于同一组中的一组特定权限的逻辑。
表1.危险权限和权限组。
许可组 | 权限 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|