赞
踩
目录
4、如何在Android 10.0及以上系统正确的读写文件?
1.3、如何在Android 11.0系统上声明你的应用与其他应用交互
1、Display.getRealSize 和 getRealMetrics:废弃和限制
8、在 BluetoothAdapter 中强制执行 BLUETOOTH_CONNECT 权限
官方文档:Android 6.0
权限分为普通权限和危险权限,危险权限除了在清单文件即AndroidManifest.xml文件中注册,还需要在需要权限的代码位置动态申请,以下为危险权限清单:
分组 | 名字 | 分割线 |
---|---|---|
PHONE | android.permission.READ_PHONE_STATE | |
android.permission.CALL_PHONE | ||
android.permission.READ_CALL_LOG | ||
android.permission.ADD_VOICEMAIL | ||
android.permission.WRITE_CALL_LOG | ||
android.permission.USE_SIP | ||
android.permission.PROCESS_OUTGOING_CALLS | ||
CALENDAR | android.permission.READ_CALENDAR | |
android.permission.WRITE_CALENDAR | ||
CAMERA | android.permission.CAMERA | |
CONTACTS | android.permission.READ_CONTACTS | |
android.permission.WRITE_CONTACTS | ||
android.permission.GET_ACCOUNTS | ||
LOCATION | android.permission.ACCESS_FINE_LOCATION | |
android.permission.ACCESS_COARSE_LOCATION | ||
MICROPHONE | android.permission.RECORD_AUDIO | |
SENSORS | android.permission.BODY_SENSORS | |
SMS | android.permission.SEND_SMS | |
android.permission.RECEIVE_SMS | ||
android.permission.READ_SMS | ||
android.permission.RECEIVE_WAP_PUSH | ||
android.permission.RECEIVE_MMS | ||
STORAGE | android.permission.READ_EXTERNAL_STORAGE | |
android.permission.WRITE_EXTERNAL_STORAGE |
- <!-- 危险权限 start -->
- <!--PHONE-->
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-permission android:name="android.permission.CALL_PHONE"/>
- <uses-permission android:name="android.permission.READ_CALL_LOG"/>
- <uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
- <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
- <uses-permission android:name="android.permission.USE_SIP"/>
- <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
- <!--CALENDAR-->
- <uses-permission android:name="android.permission.READ_CALENDAR"/>
- <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
- <!--CAMERA-->
- <uses-permission android:name="android.permission.CAMERA"/>
- <!--CONTACTS-->
- <uses-permission android:name="android.permission.READ_CONTACTS"/>
- <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
- <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
- <!--LOCATION-->
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- <!--MICROPHONE-->
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
- <!--SENSORS-->
- <uses-permission android:name="android.permission.BODY_SENSORS"/>
- <!--SMS-->
- <uses-permission android:name="android.permission.SEND_SMS"/>
- <uses-permission android:name="android.permission.RECEIVE_SMS"/>
- <uses-permission android:name="android.permission.READ_SMS"/>
- <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
- <uses-permission android:name="android.permission.RECEIVE_MMS"/>
- <!--STORAGE-->
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <!-- 危险权限 Permissions end -->
Android 6.0 版本删除了对 Apache HTTP 客户端的支持,要继续使用 Apache HTTP API,必须在build.gradle
文件中声明以下编译时依赖项
- android {
- useLibrary 'org.apache.http.legacy'
- }
对 APK 执行更严格的验证。如果文件在清单中声明但在 APK 本身中不存在,则认为 APK 已损坏。如果删除了任何内容,则必须重新签署 APK。
通过 USB 端口连接的设备现在默认设置为仅充电模式。要通过 USB 连接访问设备及其内容,用户必须明确授予此类交互的权限。如果您的应用支持通过 USB 端口与设备进行用户交互,请考虑必须显式启用交互。
官方文档:Android 7.0
在 Android 7.0 中,添加了即时 (JIT) 编译器,对 ART 进行代码分析,让它可以在应用运行时持续提升 Android 应用的性能。JIT 编译器对 Android 运行组件当前的 Ahead of Time (AOT) 编译器进行了补充,有助于提升运行时性能,节省存储空间,加快应用更新和系统更新速度。
屏幕关闭片刻后,设备在使用电池时,低电耗模式将限制网络访问,同时延迟作业和同步。在短暂的维护时间范围后,其允许应用访问网络,并执行延迟的作业/同步。打开屏幕或将设备插入电源会使设备退出低电耗模式。
SurfaceView
类可减少屏幕合成对电池的消耗,因为它是在专用硬件中合成,与应用窗口内容分隔开。因此,它产生的中间副本少于 TextureView
。从 Android 7.0 开始,强烈建议您使用 SurfaceView
代替 TextureView
,以实现省电。
Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 和 Android Plugin for Gradle 2.2 会使用 APK Signature Scheme v2 和传统签名方案来签署您的应用。
- android {
- ...
- defaultConfig { ... }
- signingConfigs {
- release {
- storeFile file("myreleasekey.keystore")
- storePassword "password"
- keyAlias "MyReleaseKey"
- keyPassword "password"
- v2SigningEnabled true //打开v2签名配置
- }
- }
- }
Android 使用的文件系统类似于其他平台上基于磁盘的文件系统。该系统提供了以下几种保存应用数据的选项:
内容类型 | 访问方法 | 所需权限 | 其他应用是否可以访问? | 卸载应用时是否移除文件? | |
---|---|---|---|---|---|
应用专属文件 | 仅供您的应用使用的文件 | 从内部存储空间访问,可以使用 getFilesDir() 或 getCacheDir() 方法从外部存储空间访问,可以使用 getExternalFilesDir() 或 getExternalCacheDir() 方法 | 从内部存储空间访问不需要任何权限 如果应用在搭载 Android 4.4(API 级别 19)或更高版本的设备上运行,从外部存储空间访问不需要任何权限 | 否 | 是 |
媒体 | 可共享的媒体文件(图片、音频文件、视频) | MediaStore API | 在 Android 11(API 级别 30)或更高版本中,访问其他应用的文件需要 READ_EXTERNAL_STORAGE 在 Android 10(API 级别 29)中,访问其他应用的文件需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 在 Android 9(API 级别 28)或更低版本中,访问所有文件均需要相关权限 | 是,但其他应用需要 READ_EXTERNAL_STORAGE 权限 | 否 |
文档和其他文件 | 其他类型的可共享内容,包括已下载的文件 | 存储访问框架 | 无 | 是,可以通过系统文件选择器访问 | 否 |
应用偏好设置 | 键值对 | Jetpack Preferences 库 | 无 | 否 | 是 |
数据库 | 结构化数据 | Room 持久性库 | 无 | 否 | 是 |
应用间共享文件适配参考:Android 7.0 行为变更 通过FileProvider在应用间共享文件吧
官方文档:Android 8.0
使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:
相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。
使用 TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:
适配方式为需要在之前的基础上加上判断:
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- }else {
- mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
- }
同时需要添加权限:
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
在 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,则系统会立即授予该权限,而不会提示用户。
Android 8.0 改变了标识符的处理方式。
要借助一个简单的标准系统实现应用获利,请使用广告 ID。广告 ID 是 Google Play 服务针对广告服务提供的唯一 ID,此 ID 可由用户重置。
针对 8.0 的应用需要在 AndroidManifest.xml 中声明 REQUEST_INSTALL_PACKAGES 权限,否则将无法进行应用内升级。
<uses-permissionandroid:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
只有不透明的全屏Activity可以自主设置界面方向,在全透明的Activity属性中设置横竖屏,会直接闪退,比如:
- <activity android:name=".TranslucentCrashTestActivity"
- android:theme="@style/TranslucentCrashTestTheme"
- android:screenOrientation="portrait"/>
此为谷歌8.0上的bug,在8.1版本上已修复。
移除掉了所有的隐式广播(即清单文件中注册的广播),所以请使用显示广播进行注册(即代码注册)。
- //只需要将渠道加进去,并且在NotificationCompat.Build中加入渠道id
- NotificationManager systemService = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
- NotificationChannel notificationChannel = new NotificationChannel("001", "测试", NotificationManager.IMPORTANCE_MAX);
- systemService.createNotificationChannel(notificationChannel);
- mBuilder = new NotificationCompat.Builder(this, "001");
- }else {
- mBuilder = new NotificationCompat.Builder(this);
- }
- Notification build = mBuilder.setContentTitle("标题").setContentText("内容").setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).build();
- systemService.notify(1,build);
8.0以后不能使用 MODE_WORLD_READABLE 方式创建SharedPreferences对象,否则会闪退,比如:
SharedPreferences read = getSharedPreferences(RELEASE_POOL_DATA, MODE_WORLD_READABLE)
需要将第二个参数修改成 MODE_PRIVATE。
处于空闲状态时的应用不可以通过startService()启动服务,当然如果是前台服务则不会被限制,可以通过Context.startForegroundService() 来启动一个前台服务,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的
startForeground() 函数。
所以在Android 8.0 上启动服务时需要做如下判断:
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(service);
- }else {
- startService(service);
- }
官方文档:Android 9.0
Android 9.0 限制了明文流量(即HTTP接口)的网络请求,非加密的流量请求都会被系统禁止,或者直接使用HTTPS接口。
解决:
方式一:在Application属性中添加如下配置:
- <application
- android:usesCleartextTraffic="true">
方式二:在res目录下新建xml文件夹,并创建network_security_config.xml文件,内容如下:
- <network-security-config>
- <base-config cleartextTrafficPermitted="true" />
- </network-security-config>
之后在Application属性中配置 :
- <application
- android:networkSecurityConfig="@xml/network_security_config">
Android 9.0 彻底废弃Apache HTTP库,如果想要继续使用,需要在清单文件中添加如下配置:
- <application>
- <uses-library android:name="org.apache.http.legacy" android:required="false"/>
- </application>
将 READ_CALL_LOG、WRITE_CALL_LOG 和 PROCESS_OUTGOING_CALLS 权限移入该组。 在之前的 Android 版本中,这些权限位于 PHONE 权限组。
对于需要访问通话敏感信息(如读取通话记录和识别电话号码)的应用,该 CALL_LOG 权限组为用户提供了更好的控制和可见性。
如果您的应用需要访问通话记录或者需要处理去电,则您必须向 CALL_LOG 权限组明确请求这些权限。 否则会发生 SecurityException 异常。
在未首先获得 READ_CALL_LOG 权限的情况下,除了应用的用例需要的其他权限之外,运行于 Android 9.0 上的应用无法读取电话号码或手机状态。
要通过 PHONE_STATE Intent 操作读取电话号码,同时需要 READ_CALL_LOG 权限和 READ_PHONE_STATE 权限。
要从 onCallStateChanged() 中读取电话号码,只需要 READ_CALL_LOG 权限。 不需要 READ_PHONE_STATE 权限。
从Android 9.0开始,只有当Intent flag中指定了FLAG_ACTIVITY_NEW_TASK,才允许在非Activity场景启动Activity。如果不在Intent添加FLAG_ACTIVITY_NEW_TASK,将无法通过非Activity的Context启动一个Activity,并且会抛异常。
App后台运行时,以下行为受限:
getConnectionInfo() 函数返回的 WifiInfo 对象受限,只有当App具有以下权限时,才能获得SSID和BSSID:
在 Android 9.0 中,下列事件和广播不接收用户位置或个人可识别数据方面的信息:
Wi-Fi 的 NETWORK_STATE_CHANGED_ACTION系统广播不再包含 SSID(之前为 EXTRA_SSID)、BSSID(之前为 EXTRA_BSSID)或连接信息(之前为 EXTRA_NETWORK_INFO)。 如果应用需要此信息,请改为调用 getConnectionInfo()。
如果用户在运行 Android 9.0上的设备停用设备定位,则以下函数不提供结果:
Android 8.0 增加startForegroundService()来创建一个前台服务,在Android 9.0 调用该函数时,还需要额外添加FOREGROUND_SERVICE权限,这是普通权限,否则会发生 SecurityException 异常。
为帮助确保应用稳定性和兼容性,Android 9.0 对某些非 SDK 函数和字段的使用进行了限制;无论你是直接访问这些函数和字段,还是通过反射或 JNI 访问,这些限制均适用。 在 Android 9.0 中,你的应用可以继续访问这些受限的接口;该平台通过 toast 和日志条目提醒你注意这些接口。 如果你的应用显示这样的 toast,则必须寻求受限接口之外的其他实现策略。下表说明了当你的应用尝试访问屏蔽名单中的非 SDK 接口时可能会出现的预期行为:
访问方式 | 结果 |
---|---|
Dalvik 指令引用某个字段 | 抛出 NoSuchFieldError |
Dalvik 指令引用某个方法 | 抛出 NoSuchMethodError |
通过 Class.getDeclaredField() 或 Class.getField() 进行反射 | 抛出 NoSuchFieldException |
通过 Class.getDeclaredMethod() 、Class.getMethod() 进行反射 | 抛出 NoSuchMethodException |
通过 Class.getDeclaredFields() 、Class.getFields() 进行反射 | 结果中未获取到非 SDK 成员 |
通过 Class.getDeclaredMethods() 、Class.getMethods() 进行反射 | 结果中未获取到非 SDK 成员 |
通过 env->GetFieldID() 进行 JNI 调用 | 返回 NULL ,抛出 NoSuchFieldError |
通过 env->GetMethodID() 进行 JNI 调用 | 返回 NULL ,抛出 NoSuchMethodError |
在 Android 9.0 中,Build.SERIAL 始终设置为 "UNKNOWN" 以保护用户的隐私。如果你的应用需要访问设备的硬件序列号,你应改为请求 READ_PHONE_STATE 权限,然后调用 getSerial()。
为改善 Android 9.0 中的应用稳定性和数据完整性,应用无法再让多个进程共用同一 WebView 数据目录。 此类数据目录一般存储 Cookie、HTTP 缓存以及其他与网络浏览有关的持久性和临时性存储。
在大多数情况下,你的应用只应在一个进程中使用 android.webkit 软件包中的类,例如 WebView 和 CookieManager。 例如,你应该将所有使用 WebView 的 Activity 对象移入同一进程。 你可以通过在应用的其他进程中调用 disableWebView(),更严格地执行“仅限一个进程”规则。 该调用可防止 WebView 在这些其他进程中被错误地初始化,即使是从依赖内容库进行的调用也能防止。
如果你的应用必须在多个进程中使用 WebView 的实例,则必须先利用 WebView.setDataDirectorySuffix() 函数为每个进程指定唯一的数据目录后缀,然后再在该进程中使用 WebView 的给定实例。 该函数会将每个进程的网络数据放入其在应用数据目录内自己的目录中。
注:即使你使用 setDataDirectorySuffix(),系统也不会跨应用的进程界限共享 Cookie 以及其他网络数据。 如果应用中的多个进程需要访问同一网络数据,你需要自行在这些进程之间复制数据。 例如,你可以调用 getCookie() 和 [setCookie()]
从 Android 9.0 开始,不再允许应用直接读取 /proc/net/xt_qtaguid 文件夹中的文件。 这样做是为了确保与某些根本不提供这些文件的设备保持一致。依赖这些文件的公开 API TrafficStats 和 NetworkStatsManager 继续按照预期方式运行。 但是,不受支持的 cutils 函数(例如 qtaguid_tagSocket())在不同设备上可能不会按照预期方式运行 , 甚至根本不运行。
- if (Build.VERSION.SDK_INT >= 28) {
- canvas.clipPath(mPath);
- } else {
- canvas.clipPath(mPath, Region.Op.REPLACE);
- }
Android 9.0 新增了对 APK Signature Scheme v3 的支持。该架构提供的选择可以在其签名块中为每个签名证书加入一条轮转证据记录。 利用此功能,应用可以通过将 APK 文件过去的签名证书链接到现在签署应用时使用的证书,从而使用新签名证书来签署应用。
注:运行 Android 8.1(API 级别 27)或更低版本的设备不支持更改签名证书。 如果应用的 minSdkVersion 为 27 或更低,除了新签名之外,可使用旧签名证书来签署应用。
详细了解如何使用 apksigner 轮转密钥。
官方文档:Android 10.0
如果在 Android 10 上运行的应用将 null 传递给 setSSLSocketFactory(),则会出现 IllegalArgumentException。在以前的版本中,将 null 传递给 setSSLSocketFactory() 与传入当前的默认 SSL 套接字工厂效果相同。
从 Android 10 开始,将弃用 android.preference 库。开发者应该改为使用 AndroidX preference 库,这是 Android Jetpack 的一部分。
Android 10.0(Q) 在外部存储设备中为每个应用提供了一个“隔离存储沙盒”(例如 /sdcard)。任何其他应用都无法直接访问你应用的沙盒文件。由于文件是你应用的私有文件,因此你不再需要任何权限即可在外部存储设备中访问和保存自己的文件。此变更可让你更轻松地保证用户文件的隐私性,并有助于减少应用所需的权限数量。
沙盒,简单而言就是应用专属文件夹,并且访问这个文件夹无需权限。谷歌官方推荐应用在沙盒内存储文件的地址为Context.getExternalFilesDir()下的文件夹。比如要存储一张图片,则应放在Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)中。
所以请判断当应用运行在Q平台上时,取消对READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE两个权限的申请。并替换为新的媒体特定权限。
Android 提供两类物理存储位置:内部存储空间和外部存储空间。在大多数设备上,内部存储空间小于外部存储空间。不过,所有设备上的内部存储空间都是始终可用的,因此在存储应用所依赖的数据时更为可靠。
可移除卷(例如 SD 卡)在文件系统中属于外部存储空间。Android 使用路径(例如 /sdcard)表示这些存储设备。默认情况下,应用本身存储在内部存储空间中。不过,如果你的 APK 非常大,也可以在应用的清单文件中指明偏好设置,以便将应用安装到外部存储空间:
- <manifest ...
- android:installLocation="preferExternal">
- ...
- </manifest>
共享存储空间又分为以下类型:
3.1、内部存储空间:
- //getFilesDir() = /data/data/应用包名/files
- File file = new File(context.getFilesDir(), filename);
-
- //getCacheDir() = /data/data/应用包名/cache
- File file = new File(context.getCacheDir(), filename);
注:getFilesDir和getCacheDir目录,为应用的内部存储专属目录,其他应用不可访问,应用自己本身读写不需要权限。
3.2、外部存储空间:
- //getExternalFilesDir = /storage/emulated/0/Android/data/应用包名/files
- File externalFileDir = new File(context.getExternalFilesDir(null), filename);
-
- //getExternalCacheDir = /storage/emulated/0/Android/data/应用包名/cache
- File externalCacheFile = new File(context.getExternalCacheDir(), filename);
注:在Android 10.0及以后,getExternalFilesDir和getExternalCacheDir目录被划分为应用的外部存储空间的专属目录,应用自己本身读写不需要权限。
3.3、共享存储空间:
- //getRootDirectory = /system
- File file = new File(Environment.getRootDirectory().getAbsolutePath());
-
- //getDataDirectory = /data
- File file = new File(Environment.getDataDirectory().getAbsolutePath());
-
- //getStorageDirectory = /storage
- File file = new File(Environment.getStorageDirectory().getAbsolutePath());
-
- //getExternalStorageDirectory = /storage/emulated/0
- File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
-
- //getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) = /storage/emulated/0/Download
- //该目录属于getExternalStorageDirectory()的子目录
- File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
注:getRootDirectory()、getDataDirectory()、getStorageDirectory()目录应用在任何Android版本都不可访问。
getExternalStorageDirectory()目录访问权限分为以下三种情况:
1、Android 9.0及以下:在申请到READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限后可以进行读写。
2、Android10.0:在申请到READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限,并且在清单文件配置停止分区存储后可以进行读写,两个条件缺一不可。配置停止分区存储方式如下:
- <!-- 停止分区存储只限制在Android 10.0起作用,在Android 11.0及以上版本该属性无效,会默认开启分区存储 -->
- <application
- android:requestLegacyExternalStorage="true">
- </application>
3、Android10.0以上:无论是否申请到READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限,或者已配置停止分区存储,应用都不可以访问。如果想要继续访问getExternalStorageDirectory()目录中的文件,需要改为申请另一种权限:MANAGE_EXTERNAL_STORAGE,该权限拥有对共享存储空间(即:/storage/emulated/0)目录下的中所有文件的读写访问权限。申请方式如下为:
获得 MANAGE_EXTERNAL_STORAGE 权限后,getExternalStorageDirectory(即:/storage/emulated/0)目录下的文件除了 /storage/emulated/0/Android/data/ 目录外,应用都拥有写入和读写权限,此写入权限包括文件路径访问权限。但是获得此权限的应用仍然无法访问属于其他应用的应用专属目录,因为这些目录在存储卷上显示为 /storage/emulated/0/Android/data/ 的子目录。
4.1、浏览设备上的图库,并返回图片地址
可以使用照片选择器。照片选择器是作为使用媒体库的替代方案,Android 照片选择器工具为用户提供了安全的内置媒体文件选择方式,让用户无需向应用授予对整个媒体库的访问权限。此功能仅适用于支持的设备。具体使用请参阅照片选择器指南。
4.2、读写图片、音频、视频
读写图片和音视频分为两种情况:
1、读写自己应用创建的媒体文件
在搭载 Android 10 或更高版本的设备上,无需任何存储相关权限即可访问和修改你自己应用拥有的媒体文件,包括 MediaStore.Downloads 集合中的文件。例如,如果你正在开发一款相机应用,则无需请求存储相关权限,因为你的应用拥有你将写入媒体库的图片。
2、读写其他应用创建的媒体文件
如需访问其他应用创建的媒体文件,你必须声明适当的存储相关权限,并且文件必须位于以下任一媒体集合中:
这三个媒体目录所对应的权限为:
- <!-- 仅当你的应用程序需要访问其他应用程序创建的图像或照片时才需要 -->
- <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
-
- <!-- 仅当你的应用程序需要访问其他应用程序创建的视频时才需要 -->
- <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
-
- <!-- 仅当你的应用程序需要访问其他应用程序创建的音频时才需要 -->
- <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
注:Android 10.0以下系统,则需要请求READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限才能访问任何媒体文件,Android 10.0及以上系统不再请求这两个权限。
4.3、读写非媒体文件
读写非媒体文件,比如创建和读写文档类型的文件,可以使用Android 10.0提供的存储访问框架,在该框架支持下,创建、修改、访问你所创建的文档文件,不需要任何系统权限。
存储访问框架支持以下访问文件和其他文档的用例。
具体使用请参考:从共享存储空间访问文档和其他文件
为了让用户更好地控制应用对位置信息的访问权限,Android 10.0 引入了 ACCESS_BACKGROUND_LOCATION 权限。
与 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 权限不同,ACCESS_BACKGROUND_LOCATION 权限仅会影响应用在后台运行时对位置信息的访问权限。除非符合以下条件之一,否则应用将被视为在后台访问位置信息:
如果你的应用在 Android 10 或更高版本上运行,但其目标平台是 Android 9(API 级别 28)或更低版本,则该平台具有以下行为:
简而言之:如果应用有后台服务,并且需要访问位置信息,比如滴滴,那么就需要申请ACCESS_BACKGROUND_LOCATION 权限。
在启动新的Activity时,只有在满足以下条件至少一种时才可启动,具体可参考:限制启动Activity的例外情况
默认情况下,在搭载 Android 10 或更高版本的设备上,系统会传输随机分配的 MAC 地址。
在搭载 Android 10 或更高版本的设备上,应用无法访问 /proc/net,其中包含与设备的网络状态相关的信息。需要访问这些信息的应用(如 VPN)应使用 NetworkStatsManager 或 ConnectivityManager 类。
从 Android 10 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE
特许权限才能访问设备的不可重置标识符(包含 IMEI 和序列号)。
受影响的方法包括:
如果您的应用没有该权限,但你仍尝试查询不可重置标识符的相关信息,则平台的响应会因目标 SDK 版本而异:
除非您的应用是默认输入法 (IME) 或是目前处于焦点的应用,否则它无法访问 Android 10 或更高版本平台上的剪贴板数据。
如果您的应用以 Android 10 或更高版本为目标平台,则该应用只能在用户授予其访问 USB 设备或配件的权限后才能读取序列号。
如果您的应用以 Android 5.1(API 级别 22)或更低版本为目标平台,则用户首次在搭载 Android 10 或更高版本的平台上使用您的应用时,系统会向其显示当前应用所使用到的权限列表界面,此界面让用户有机会撤消系统先前在安装时向应用授予的访问权限。
具体参考官方文档:Android 10.0 中的隐私权变更
官方文档:Android 11.0
Android 11.0 更改了应用查询用户已在设备上安装的其他应用以及与之交互的方式。使用 <queries> 元素,应用可以定义一组自身可访问的其他软件包。通过告知系统应向你的应用显示哪些其他软件包,此元素有助于鼓励最小权限原则。
如果你的应用以 Android 11 或更高版本为目标平台,你可能需要在应用的清单文件中添加 <queries> 元素。在 <queries> 元素中,你可以按软件包名称、intent 签名或提供程序授权指定软件包。
即使你的应用以 Android 11(API 级别 30)或更高版本为目标平台,以下类型的应用也始终对你的应用可见:
此外,你可以使用隐式或显式 intent 来启动另一应用的 activity,无论这个应用是否对你的应用可见。
实现 Android 核心功能的某些系统软件包会自动对你的应用可见,即使您的应用以 Android 11.0(API 级别 30)或更高版本为目标平台也是如此。这组特定的软件包取决于运行你应用的设备。
如需查看特定设备的完整软件包列表,请在开发机器上的终端中运行以下命令:
adb shell dumpsys package queries
如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,并且需要与应用(自动可见的应用除外)交互,请在您应用的清单文件中添加 <queries> 元素。
- <manifest package="com.example.game">
- <queries>
- <package android:name="com.example.store" />
- <package android:name="com.example.services" />
- </queries>
- ...
- </manifest>
- <manifest package="com.example.game">
- <queries>
- <intent>
- <action android:name="android.intent.action.SEND" />
- <data android:mimeType="image/jpeg" />
- </intent>
- </queries>
- ...
- </manifest>
- <manifest package="com.example.suite.enterprise">
- <queries>
- <provider android:authorities="com.example.settings.files" />
- </queries>
- ...
- </manifest>
- <queries>
- <!-- Android 11.0 系统跳转系统浏览器时需要 -->
- <intent>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.BROWSABLE" />
- <data android:scheme="https" />
- </intent>
- </queries>
如果应用需要处理文件或附件,例如需要检查设备能否打开给定文件,通常最简单的办法是尝试启动可以处理该文件的 Activity。为此,请使用包含 ACTION_VIEW intent 操作和代表特定文件的 URI 的 intent。如果设备上没有可用的应用,你的应用可捕获 ActivityNotFoundException。在异常处理逻辑中,你可以显示错误或尝试自行处理相关文件。
如果你的应用必须事先知道其他应用能否打开给定文件,请在以下代码段中将 <intent> 元素作为 <queries> 元素的一部分添加到清单中。如果你在编译时已经知道文件类型是什么,请包括文件类型。然后,你可以使用 intent 调用 resolveActivity()
来检查是否有可用应用。
- <!-- Place inside the <queries> element. -->
- <intent>
- <action android:name="android.intent.action.VIEW" />
- <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
- <data android:mimeType="application/pdf" />
- </intent>
具体用例可参考官网:根据用例配置软件包可见性
注:使用<queries>
元素需具备以下条件:
在 Android 11.0 上运行但以 Android 10(API 级别 29)为目标平台的应用仍可请求 requestLegacyExternalStorage 属性。应用可以利用此标记暂时停用与分区存储相关的变更,例如授予对不同目录和不同类型的媒体文件的访问权限。当你将应用更新为以 Android 11 为目标平台后,系统会忽略 requestLegacyExternalStorage 标记,强制开启分区存储。
注意:如果在Android 11.0(API 级别 30)上运行,但是应用覆盖安装的话,可以增加android:preserveLegacyExternalStorage=“true”属性,可以暂时关闭分区存储,但是一旦用户卸载重装,该属性就会失效,重新安装后的应用会开启分区存储。
从 Android 11 开始,应用无法在外部存储设备上创建自己的应用专用目录。如需访问系统为您的应用提供的目录,请调用 getExternalFilesDirs()。如果访问系统中的应用专属缓存目录,请调用getExternalCacheDir()。并且在 Android 11.0 上,应用无法再访问外部存储设备中的任何其他应用的专用于特定应用的目录中的文件。
Android 11 添加了 API 以查询对同时使用多个摄像头(包括前置摄像头和后置摄像头)的支持。
如需在运行应用的设备上检查支持情况,请使用以下方法:
Android 11.0 引入了 ActivityManager.getHistoricalProcessExitReasons() 方法,用于报告近期任何进程终止的原因。应用可以使用此方法收集崩溃诊断信息,例如进程终止是由于 ANR、内存问题还是其他原因所致。此外,你还可以使用新的 setProcessStateSummary() 方法存储自定义状态信息,以便日后进行分析。
getHistoricalProcessExitReasons() 方法会返回 ApplicationExitInfo 类的实例,该类包含与应用进程终止相关的信息。通过对此类的实例调用 getReason(),你可以确定应用进程终止的原因。例如,返回值为 REASON_CRASH 表示你的应用中发生了未处理的异常。如果您的应用需要确保退出事件的唯一性,它可以维护一个应用专用的标识符,如基于来自 getTimestamp() 方法的时间戳的哈希值。
注意:某些设备无法报告内存不足终止事件。在这些设备上,getHistoricalProcessExitReasons() 方法会返回 REASON_SIGNALED 而不是 REASON_LOW_MEMORY,并且 getStatus() 的返回值为 SIGKILL。
如需检查设备是否可以报告内存不足终止事件,请调用 ActivityManager.isLowMemoryKillReportSupported()。
Android 11.0 添加了对 APK 签名方案 v4 的支持。此方案会在单独的文件 (apk-name.apk.idsig) 中生成一种新的签名,但在其他方面与 v2 和 v3 类似。没有对 APK 进行任何更改。此方案支持 ADB 增量 APK 安装,这样会加快 APK 安装速度。
在 Android 11.0 及更高版本中,使用 getIccId() 方法访问不可重置的 ICCID 受到限制。该方法会返回一个非 null 的空字符串。如需唯一标识设备上安装的 SIM 卡,请改用 getSubscriptionId() 方法。订阅 ID 会提供一个索引值(从 1 开始),用于唯一识别已安装的 SIM 卡(包括实体 SIM 卡和电子 SIM 卡)。除非设备恢复出厂设置,否则此标识符的值对于给定 SIM 卡是保持不变的。
Android 11.0 更改了应用在读取电话号码时使用的与电话相关的权限。
也就是当用到这两个API的时候,原来的READ_PHONE_STATE权限不管用了,需要添加READ_PHONE_NUMBERS权限并且动态获取之后才行。
Android 11.0上不允许后台显示自定义视图的Toast,如果想要从后台显示Toast,可以使用普通Toast。
对于以 Android 11.0(API 级别 30)为目标平台,且目前仅使用 APK 签名方案 v1 签名的应用,现在还必须使用 APK 签名方案 v2 或更高版本进行签名。用户无法在搭载 Android 11 的设备上安装或更新仅通过 APK 签名方案 v1 签名的应用。
从 Android 11.0 开始,只有预装的系统相机应用可以响应以下 intent 操作:
也就是说,如果我调用intent唤起照相机,使用VIDEO_CAPTURE的action,只有系统的相机能够响应,而第三方的相机应用不会响应了。
- //无法唤起第三方相机了,只能唤起系统相机
- Intent intent = new Intent();
- intent.setAction(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
- startActivity(intent);
官方给的建议是如果要使用特定的第三方相机应用来代表其捕获图片或视频,可以通过为intent设置软件包名称或组件来使这些intent变得明确。
如果应用的功能在下列某种情况下访问设备的当前位置信息,系统就会认为应用需要使用前台位置信息:
此外,建议你声明 location 的前台服务类型,如以下代码段所示。在 Android 10(API 级别 29)及更高版本中,你必须声明此前台服务类型。
- <service
- android:name="MyNavigationService"
- android:foregroundServiceType="location">
- </service>
当应用请求 ACCESS_COARSE_LOCATION 权限或 ACCESS_FINE_LOCATION 权限时(如以下代码段所示),就是在声明需要获取前台位置信息:
- <manifest ... >
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <!-- 仅当你的应用程序用于精确位置访问时添加 -->
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- </manifest>
除了前台位置信息部分所述的两种情况之外,如果应用在任何其他情况下访问设备的当前位置信息,系统就会认为应用需要使用后台位置信息。后台位置信息精确度与前台位置信息精确度相同,具体取决于应用声明的位置信息权限。
在 Android 10.0(API 级别 29)及更高版本中,你必须在应用的清单中声明 ACCESS_BACKGROUND_LOCATION 权限,以便请求在运行时于后台访问位置信息。在较低版本的 Android 系统中,当应用获得前台位置信息访问权限时,也会自动获得后台位置信息访问权限。
- <manifest ... >
- <!-- 仅在请求后台位置访问时才需要,且是在Android 10(API级别29)及更高版本。 -->
- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- </manifest>
注意:申请后台位置权限,要在获取前台权限之后,顺序不能乱。
Android 10.0(api 29)上通可以过SAF(存储访问框架–Storage Access Framework)来访问外部存储上的公共目录,但是Android 11.0(api 30)再次升级,部分目录和文件不能访问了,具体如下:
无法再使用 ACTION_OPEN_DOCUMENT_TREE intent 操作请求访问以下目录:
无法再使用 ACTION_OPEN_DOCUMENT_TREE 或 ACTION_OPEN_DOCUMENT intent 操作请求用户从以下目录中选择单独的文件:
如果你的应用以 Android 11.0(api 30) 为目标平台,你将无法再使用 allowBackup 属性停用应用文件的设备到设备迁移。系统会自动启用此功能。不过,即使你的应用以 Android 11.0(api 30) 为目标平台,你也可以通过将 allowBackup 属性设置为 false 来停用应用文件的云端备份和恢复。
android:allowBackup属性:
从 Android 11.0(API 级别 30)开始,每当你的应用请求与位置、麦克风或相机相关的权限时,面向用户的权限对话框都会包含仅限这一次选项,如果用户在对话框中选择此选项,系统会向应用授予临时的单次授权。然后,应用可以在一段时间内访问相关数据,具体时间取决于应用的行为和用户的操作:
如果用户撤消单次授权(例如在系统设置中撤消),无论你是否启动了前台服务,应用都无法访问相关数据。与任何权限一样,如果用户撤消了应用的单次授权,应用进程就会终止。当用户下次打开应用并且应用中的某项功能请求访问位置信息、麦克风或摄像头时,系统会再次提示用户授予权限。简单的说,就是在申请与位置信息、麦克风或摄像头相关的权限时,系统会自动提供一个单次授权的选项,只供这一次权限获取。然后用户下次打开app的时候,系统会再次提示用户授予权限。
官方文档:Android 12.0
在 Android 11.0 中,我们引入了 WindowMetrics API 并废弃了以下方法:
在 Android 12.0 中,我们继续建议使用 WindowMetrics,并且正在逐步废弃以下方法:
在搭载 Android 12.0 或更高版本的设备上,用户可以要求你的应用只能访问大致位置信息。
粗略位置: 精确到2平方公里的位置值,请求 ACCESS_COARSE_LOCATION 权限可以获得。
精确位置: 精确到50米以内的位置值,请求 ACCESS_FINE_LOCATION 权限可以获得。
Android 12.0 引入了 BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT 权限。这些权限可让以 Android 12.0 为目标平台的应用更轻松地与蓝牙设备互动,尤其是不需要访问设备位置信息的应用。
为了加强用户与应用和系统互动时的控制,从 Android 12.0 开始,弃用了 ACTION_CLOSE_SYSTEM_DIALOGS intent 操作。除了一些特殊情况之外,当应用尝试调用包含此操作的 intent 时,系统会基于应用的目标 SDK 版本执行以下操作之一:
- E ActivityTaskManager Permission Denial: \
- android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from \
- com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, \
- dropping broadcast.
在以下情况下,应用仍然可以在 Android 12.0 或更高版本上关闭系统对话框:
Android 12.0 更改了在按下“返回”按钮时系统对为其任务根的启动器 activity 的默认处理方式。在以前的版本中,系统会在按下“返回”按钮时finsh这些 activity。在 Android 12.0 中,现在系统会将 activity 及其任务移到后台,而不是finsh activity。当使用主屏幕按钮或手势从应用中导航出应用时,新行为与当前行为一致。
注意:系统仅会将新行为应用于为其任务根的启动器 activity,即使用 ACTION_MAIN 和 CATEGORY_LAUNCHER 声明 intent 过滤器的 activity。对于其他 activity,在按下“返回”按钮时,系统会像以前一样finsh activity。
对于大多数应用而言,此变更意味着使用“返回”按钮退出应用的用户可以更快地从温状态恢复应用,而不必从冷状态完全重启应用。
建议你针对此变更测试你的应用。如果你的应用目前替换 onBackPressed() 来处理返回导航并finsh Activity,请更新你的实现来调用 super.onBackPressed() 而不是finsh Activity。调用 super.onBackPressed() 可在适当时将 activity 及其任务移至后台,
Android 12 引入了 RoundedCorner 和 WindowInsets.getRoundedCorner(int position),它们可以提供圆角的半径和中心点。
具体参考:https://developer.android.google.cn/guide/topics/ui/look-and-feel/rounded-corners
为了让开发者能够更好地控制用户在与开发者的应用互动时会看到什么内容,Android 12.0 引入了隐藏由具有 SYSTEM_ALERT_WINDOW 权限的应用绘制的叠加窗口的功能。
声明 HIDE_OVERLAY_WINDOWS 权限后,应用可以调用 setHideOverlayWindows() 以指明当应用自己的窗口可见时所有 TYPE_APPLICATION_OVERLAY 类型的窗口都应隐藏。在显示敏感屏幕(如交易确认流程)时,应用可能会选择这样做。显示 TYPE_APPLICATION_OVERLAY 类型窗口的应用应考虑可能更适合其用例的替代方案,如画中画或气泡。
Android 12.0 向 android.os.Build 添加了两个常量,它们可通过 SDK 公开 SoC 芯片组供应商和型号信息。您可以通过分别调用 Build.SOC_MANUFACTURER 和 Build.SOC_MODEL 来检索此信息。
从Android 12.0 开始,用户可以通过状态栏下拉菜单中两个新增的切换开关选项,一键启用/停用摄像头和麦克风使用权限。
这里的「使用权限」针对的是设备上的所有App,是全局的,不要和Android 6.0的「运行时权限」混淆。如果用户主动关闭了摄像头或麦克风的使用权限,那么当下次App再需要启动摄像头或麦克风时,系统就会提醒用户,相关硬件的使用权限已关闭,并申请重新开启。
以Android 12.0 为目标平台的App,如果其包含的四大组件中使用到了 intent 过滤器(intent-filter),则必须显示声明 android:exported 属性,否则App将无法在 Android 12.0 及更高系统版本的设备上安装。
以 Android 12.0 为目标平台的App,在构建PendingIntent时,需要指定Flag为FLAG_IMMUTABLE
(建议)或FLAG_MUTABLE
二者之一,否则App将崩溃。
Android 12.0 对应用从后台启动前台服务的行为做出限制,除了后台启动限制的豁免 等少数情况外,如果应用尝试在后台运行时启动前台服务,系统会抛出ForegroundServiceStartNotAllowedException异常。应用可以使用WorkManager的加急工作来执行后台任务。
Android 12系统引入了新的权限 android.permission.SCHEDULE_EXACT_ALARM,设置AlarmManager 精准闹钟的应用必须在 Manifest中请求 SCHEDULE_EXACT_ALARM 权限。
新增了一个新的API — — canScheduleExactAlarms(),用于检查应用的精准闹钟权限状态。
从Android 12.0 开始,所有的App在每次启动时(特指冷启动与温启动),系统都会为我们加上一个默认的启动画面。
该启动画面主要由以下4个元素组成,分别为:
适配方案:
在SplashScreen API之前,我们通常是利用 SplashActivity 的背景图 android:windowBackground 来实现应用启动转场效果,这个大家都很熟悉了。如果你不做任何适配,那么根据你配置的 windowBackground 资源值,在 Android 12 上会有不同的效果:
因此,如果你的应用采用的是 windowBackground 为图片资源的方式,那么你不适配也没有问题。需要升级启动效果的话,可以参考:https://developer.android.google.cn/guide/topics/ui/splash-screen
Android 11.0 就已经引入了应用休眠机智,如果用户有几个月没有与应用交互,那么系统会将应用置于休眠/冬眠状态,Android 12.0 在此基础上扩展了应用休眠机智:
官方文档:Android 13.0
在Android13之前,App只需要使用NotificationManager即可向终端用户推送通知栏消息。Android13则引入了新的运行时通知权限:android.permission.POST_NOTIFICATIONS。对此,App开发者需要予以重点关注。
适配:
1、声明权限
- <manifest ...>
- <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
- <application ...>
- ...
- </application>
- </manifest>
2、代码动态申请
3、弹出通知权限对话框,对话框内容分为以下三种:
a、如果用户选择允许选项,你的应用可以执行以下操作:
b、如果用户选择“不允许”
如果用户选择不允许选项,你的应用将无法发送通知,除非该应用符合豁免条件。除了几个特定角色之外,所有通知渠道都会被屏蔽。这类似于用户在系统设置中手动关闭应用的所有通知后发生的行为。
注意:如果你的应用以 12L 或更低版本为目标平台,并且用户点按不允许(即使仅点按一次),那么系统不会再次提示用户,直到发生以下任一情况:
c、如果用户滑开对话框(即他们既没有选择允许,也没有选择不允许),通知权限的状态不会变化。
对新安装的应用的影响:
如果用户在搭载 Android 13.0 或更高版本的设备上安装您的应用,应用的通知默认处于关闭状态。在你请求新的权限且用户向你的应用授予该权限之前,你的应用都将无法发送通知。
权限对话框的显示时间取决于应用的目标 SDK 版本:
对现有应用更新的影响:
预先授予权限的资格条件:
如果用户拒绝授予通知权限,就不会在抽屉式通知栏中看到与前台服务相关的通知。 不过,无论是否授予通知权限,用户都会在 前台服务(FGS) 任务管理器 中看到与前台服务相关的通知。
无论应用采用何种目标 SDK 版本,Android 13.0(API 级别 33)都允许用户从抽屉式通知栏中停止前台服务。这项新功能称为前台服务 (FGS) 任务管理器,它会显示当前正在运行前台服务的应用列表。此列表的标签为使用中的应用。每个应用旁边都有一个停止按钮。抽屉式通知栏底部有一个按钮,用于指示当前在后台运行的应用的数量。按此按钮时,系统会显示一个对话框,其中会列出不同应用的名称。每个应用的右侧都有一个“停止”按钮。当用户在 FGS 任务管理器中按您应用旁边的停止按钮时,系统会停止您的整个应用,而不仅仅是正在运行的前台服务。当用户按停止按钮时,系统不会向您的应用发送任何回调。
比较“向上滑动”用户操作和“强行停止”用户操作的行为:
FGS 任务管理器 | 向上滑动 | 强行停止 | |
---|---|---|---|
立即从内存中移除应用 | ✔ | ✔ | |
停止媒体播放 | ✔ | ✔ | |
停止 FGS/移除关联的通知 | ✔ | ✔ | |
移除 activity 返回堆栈 | ✔ | ✔ | ✔ |
从历史记录中移除应用 | ✔ | ✔ | |
取消预定作业 | ✔ | ||
取消闹钟 | ✔ |
以下应用可以运行前台服务,而完全不会显示在任务管理器中:
当以下类型的应用运行前台服务时,它们会显示在 FGS 任务管理器中,但应用名称旁边没有可以供用户按的停止按钮:
在以前的 Android 版本中,用户需要向您的应用授予 ACCESS_FINE_LOCATION 权限,应用才能完成一些常见的 Wi-Fi 用例。
由于用户很难将位置信息权限与 Wi-Fi 功能相关联,因此 Android 13(API 级别 33)在 NEARBY_DEVICES 权限组中引入了运行时权限,适用于管理设备与附近 Wi-Fi 接入点连接情况的应用。此权限 (NEARBY_WIFI_DEVICES) 可满足以下 Wi-Fi 用例:
如果你的应用(targetSdk == 33)已经声明不会根据 WiFi信息推导设备的物理位置信息,那就不再需要声明 ACCESS_FINE_LOCATION 权限。另外,如果应用在Android13上只使用WiFi API而不使用位置信息,那开发者可以在AndroidManifest.xml中增加NEARBY_WIFI_DEVICES权限,并将usesPermissionFlags属性设为neverForLocation,给ACCESS_FINE_LOCATION权限增加maxSdkVersion="32"的限制,代码如下:
- <manifest ...>
- <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
- android:usesPermissionFlags="neverForLocation"/>
-
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
- android:maxSdkVersion="32" />
- </manifest>
如果你的应用以 Android 13.0 或更高版本为目标平台,并且需要访问其他应用已经创建的媒体文件,你必须请求以下一项或多项细化的媒体权限,而不是 READ_EXTERNAL_STORAGE 或者 WRITE_EXTERNAL_STORAGE 权限:
媒体类型 | 请求权限 |
---|---|
图片和照片 | READ_MEDIA_IMAGES |
视频 | READ_MEDIA_VIDEO |
音频文件 | READ_MEDIA_AUDIO |
单独请求READ_MEDIA_IMAGES、单独请求 READ_MEDIA_VIDEO和同时请求READ_MEDIA_IMAGES& READ_MEDIA_VIDEO,系统均将只显示一个授权弹窗。 另外,如果App(targetSdk == 33)已经申请了读的权限,那App同时也就有了写的权限,无需再额外声明 WRITE_EXTERNAL_STORAGE权限,代码如下:
- <manifest ...>
- <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
- <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
- <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
-
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
- android:maxSdkVersion="32" />
- </manifest>
Android 13.0 中引入了“在使用时”访问身体传感器(例如心率、体温和血氧饱和度)的概念。此访问模式与 Android 10.0(API 级别 29)系统为位置信息引入的模式非常相似。
如果你的应用以 Android 13 为目标平台,并且在后台运行时需要访问身体传感器信息,那么除了现有的 BODY_SENSORS 权限外,您还必须声明新的 BODY_SENSORS_BACKGROUND 权限。
为了节省系统资源,Android 12.0 引入了SCHEDULE_EXACT_ALARM权限进行“闹钟和提醒”功能的授权管理。Android 13.0 则又引入了新的闹钟权限USE_EXACT_ALARM。
和Android 12.0 的SCHEDULE_EXACT_ALARM权限不同,如果App已经申请使用了USE_EXACT_ALARM新权限,那么用户是不能在设置页面里关闭授权的。
对于日程管理、时间管理等类型的App来讲,Android 13.0 引入的USE_EXACT_ALARM权限能够带来一定便利。相比Android 12.0 的SCHEDULE_EXACT_ALARM权限,使用新权限的应用将不再需要频繁打扰用户进行授权,能够更高效地为用户提供闹钟、日程提醒等服务。
不过,为了防止新权限被滥用,GooglePlay设置了严格的上架审核机制。开发者要注意,一旦使用了USE_EXACT_ALARM权限,App在上架GooglePlay时将会被平台严格审查。除非App属于闹钟、计时器、日历等类型的应用或者在已被列入到应用市场的白名单里,否则GooglePlay将不会允许使用该权限的应用上架。
使用 Google Play 服务广告 ID 且以 Android 13(API 级别 33)及更高版本为目标平台的应用必须在其清单文件中声明常规 AD_ID权限,如下所示:
- <manifest ...>
- <!-- 仅当你的应用程序针对Android 13或更高版本时才需要 -->
- <uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
-
- <application ...>
- ...
- </application>
- </manifest>
如果你的应用以 Android 13.0 或更高版本为目标平台且未声明此权限,系统会自动移除广告 ID 并将其替换为一串零。
以往的Android系统下,应用动态注册的BroadcastReceiver广播接收器会接收到任何应用发送的广播(除非该接收器使用了应用签名权限保护),这会使动态注册的广播接收器存在安全风险。
Android 13.0 要求,应用动态注册的广播接收器必须以显著的方式指出是否允许其他应用访问,即其他应用是否可以向其发送广播。否则,在动态注册时系统将抛出安全异常SecurityException。
目前该增强措施并非默认生效,开发者需启用 DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED兼容性框架,并在动态注册广播时指定是否接受其他应用的广播:
- context.registerReceiver(receiver, intentFilter, RECEIVER_EXPORTED)
- context.registerReceiver(receiver, intentFilter, RECEIVER_NOT_EXPORTED)
注意:系统广播不受RECEIVER_NOT_EXPORTED影响。
在之前版本的Android系统中,开发者只需将android:exported设为true就可以跨应用显式启动Activity和Service,即使intent-filter中的action或者type不匹配,也能够启动。为避免上述漏洞,Android 13.0 增强了intent-filter的匹配过滤逻辑。在接收方的targetSdk == 33的情况下,如果intent-filter匹配命中,无论发送方的targetSdk版本如何,intent都将生效。
以下几种情况不需要遵循intent-filter的匹配过滤逻辑:
Android 13.0(API 级别 33)及更高版本引入了照片选择器体验。应用启动照片选择器时,用户可选择与应用分享特定图片和视频(如个人资料照片),而不是授予应用查看整个媒体库的权限。这是访问用户照片和视频的建议方法。
照片选择器可为用户提供更好的隐私保护,因为你的应用无需声明任何运行时权限。此外,照片选择器还为应用提供内置标准化界面,从而打造更一致的用户体验。
Android 13.0 可支持 APK 签名方案 v3.1,此方案在现有的 APK 签名方案 v3 的基础上进行了改进,解决了 APK 签名方案 v3 的一些已知问题。具体而言,v3.1 签名方案允许应用在单个 APK 中同时支持原始签名者和轮替签名者。此外,v3.1 签名方案还支持 SDK 版本定位功能,这会允许轮替定位到更高版本的平台。
v3.1 签名方案使用在 12L 或更低版本中无法识别的分块 ID。因此,平台会应用以下签名者行为:
尚未轮替其签名密钥的应用无需执行任何其他操作。每当这些应用选择轮替时,系统都会默认应用 v3.1 签名方案。
在 Android 14 中,当应用处于缓存状态时,系统可能会将上下文注册的广播放入队列中。这与 Android 12(API 级别 31)为异步 binder 事务引入的队列行为类似。在清单中声明的广播不会加入队列,并且应用会从缓存状态中移除以进行广播传递。
当应用离开缓存状态(例如返回前台)时,系统会传递所有已加入队列的广播。某些广播的多个实例可以合并为一个广播。根据其他因素(例如系统运行状况),系统可能会将应用从缓存状态中移除,并且系统会传送之前加入队列的所有广播。
从 Android 14 开始,当您的应用调用 killBackgroundProcesses() 时,该 API 只能终止您自己应用的后台进程。如果您传入另一个应用的软件包名称,此方法对该应用的后台进程没有影响,并且 Logcat 中会显示以下消息:
Invalid packageName: com.example.anotherapp
您的应用不应使用 killBackgroundProcesses() API,也不得以其他方式尝试影响其他应用的进程生命周期,即使在旧版操作系统上也是如此。Android 旨在让缓存应用在后台运行,并在系统需要内存时自动终止它们。如果您的应用会不必要地终止其他应用,则由于之后需要完全重启这些应用,因此可能会降低系统性能并增加耗电量,这比恢复现有缓存应用所消耗的资源要多得多。
Android 14 引入了“所选照片访问权限”,可让用户授权应用访问其媒体库中的特定图片和视频,而不是授予对给定类型的所有媒体内容的访问权限。
如果您还没有使用照片选择器,我们建议您在应用中实现该选择器,以便在选择图片和视频时能够获得一致的体验,同时还可以加强用户隐私保护,而无需请求任何存储权限。
如果您使用存储权限维护自己的图库选择器,并且需要全面控制您的实现,请调整您的实现以使用新的 READ_MEDIA_VISUAL_USER_SELECTED 权限。如果您的应用不使用新权限,系统会在兼容模式下运行您的应用。
如果您的应用向用户显示不可关闭的前台通知,请注意:Android 14 已更改此行为,允许用户关闭此类通知。
这项变更适用于通过 Notification.Builder#setOngoing(true) 或 NotificationCompat.Builder#setOngoing(true) 设置 Notification.FLAG_ONGOING_EVENT 来阻止用户关闭前台通知的应用。FLAG_ONGOING_EVENT 的行为已发生变化,使用户实际上能够关闭此类通知。
在以下情况下,此类通知仍不可关闭:
此外,这一新行为不适用于以下用例中的通知:
从 Android 14 开始,targetSdkVersion 低于 23 的应用无法安装。要求应用满足这些最低目标 API 级别要求有助于提高用户的安全性和隐私性。
恶意软件通常会以较旧的 API 级别为目标平台,以绕过在较新版本 Android 中引入的安全和隐私保护机制。例如,有些恶意软件应用使用 targetSdkVersion 22,以避免受到 Android 6.0 Marshmallow(API 级别 23)在 2015 年引入的运行时权限模型的约束。这项 Android 14 变更使恶意软件更难以规避安全和隐私权方面的改进限制。尝试安装以较低 API 级别为目标平台的应用将导致安装失败,并且 Logcat 中会显示以下消息:
INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7
在升级到 Android 14 的设备上,targetSdkVersion 低于 23 的所有应用都将继续保持安装状态。如果您需要测试以旧版 API 级别为目标平台的应用,请使用以下 ADB 命令:
adb install --bypass-low-target-sdk-block FILENAME.apk
媒体库支持查询 OWNER_PACKAGE_NAME 列,该列表示存储特定媒体文件的应用。从 Android 14 开始,除非满足以下条件之一,否则系统会隐去此值:
为了打造更加标准化的屏幕截图检测体验,Android 14 引入了可保护隐私的屏幕截图检测 API。借助此 API,应用可以按 activity 注册回调。如果用户在该 activity 可见时截取屏幕截图,系统会调用这些回调并通知用户。
注意:该回调未提供实际屏幕截图的图片。用户截取屏幕截图后,屏幕上的内容将由您的应用决定。
对于以 Android 14(API 级别 34)为目标平台的应用,Android 14 会在调用 BluetoothAdapter getProfileConnectionState() 方法时强制执行 BLUETOOTH_CONNECT 权限。
此方法已需要 BLUETOOTH_CONNECT 权限,但未被强制执行。请确保应用在应用的 AndroidManifest.xml 文件中声明 BLUETOOTH_CONNECT(如以下代码段所示),并在调用 getProfileConnectionState 之前检查用户是否已授予权限。
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
对于以 Android 14 为目标平台的应用,Android 会通过以下方式限制应用向内部应用组件发送隐式 intent:
这些变更可防止恶意应用拦截意在供应用内部组件使用的隐式 intent。例如,下面是可以在应用的清单文件中声明的 intent 过滤器:
- <activity
- android:name=".AppActivity"
- android:exported="false">
- <intent-filter>
- <action android:name="com.example.action.APP_ACTION" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
如果应用尝试使用隐式 intent 启动此 activity,则系统会抛出异常:
- // Throws an exception when targeting Android 14.
- context.startActivity(new Intent("com.example.action.APP_ACTION"));
如需启动android:exported="false"的 activity,应用应改用显式 intent:
- // This makes the intent explicit.
- Intent explicitIntent = new Intent("com.example.action.APP_ACTION")
- explicitIntent.setPackage(context.getPackageName());
- context.startActivity(explicitIntent);
以 Android 14 为目标平台并使用上下文注册的接收器的应用和服务必须指定以下标志,以指明接收器是否应导出到设备上的所有其他应用:RECEIVER_EXPORTED 或 RECEIVER_NOT_EXPORTED。此要求有助于利用 Android 13 中引入的这些接收器的功能,来保护应用免受安全漏洞的影响。
- context.registerReceiver(receiver, intentFilter, RECEIVER_EXPORTED)
- context.registerReceiver(receiver, intentFilter, RECEIVER_NOT_EXPORTED)
例外:如果您的应用仅通过 Context#registerReceiver 方法针对系统广播注册接收器,则不受上诉限制。
对于以 Android 14 为目标平台的应用,系统会进一步限制允许应用在后台启动 activity 的时间:
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。