简要
因为目前开发的项目target版本是在32,所以这边整理的内容没有包含android 13的变动,主要涉及到了定位、蓝牙等权限
READ_PHONE_STATE权限
READ_PHONE_STATE权限是允许访问电话状态权限,在android 11之后权限名称更改为READ_PHONE_NUMBERS,所以当目标版本在android11以下还是用READ_PHONE_STATE去动态申请,以 Android 11 或更高版本为目标平台则申请READ_PHONE_NUMBERS。
<manifest>
<!-- Grants the READ_PHONE_STATE permission only on devices that run
Android 10 (API level 29) and lower. -->
<uses-permission android:name="READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="READ_PHONE_NUMBERS" />
</manifest>
特别说明:在小米手机MIUI对于该权限有特殊限制,如果需要此权限需要提交申请,或引导用户将应用设置为默认拨号应用,具体可以查看小米官方说明:dev.mi.com/console/doc…
- 重要提示:以下权限,在MIUI 11 3.12 的开发版后将无法获取:
Manifest.permission.READ_PHONE_STATE
Manifest.permission.READ_PHONE_NUMBERS
Manifest.permission.CALL_PHONE Manifest.permission.ANSWER_PHONE_CALLS
蓝牙权限
android12以下,进行蓝牙扫描时,只需要在清单文件上声明以下normal级权限即可(特别说明:需要有位置权限):
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
在target为android12的版本中开发时,蓝牙扫描是需要进行权限申请的,具体蓝牙权限如下(无需位置权限):
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
如果您的应用与蓝牙设备互动,强烈建议您执行以下操作:
- 如果您的应用查找蓝牙设备(如蓝牙低功耗 (BLE) 外围设备),请向应用的清单中添加
BLUETOOTH_SCAN权限。 - 如果您的应用使当前设备可被其他蓝牙设备检测到,请向应用的清单中添加
BLUETOOTH_ADVERTISE权限。 - 如果您的应用与已配对的蓝牙设备通信,请向应用的清单中添加
BLUETOOTH_CONNECT权限。 - 对于旧版蓝牙相关的权限声明,请将
android:maxSdkVersion设为30。此应用兼容性步骤有助于系统仅向您的应用授予在搭载 Android 12 的设备上安装时所需的蓝牙权限。
以下代码段演示了如何在您的应用中声明这些新权限:
<manifest>
<!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<!-- Needed only if your app looks for Bluetooth devices.
You must add an attribute to this permission, or declare the
ACCESS_FINE_LOCATION permission, depending on the results when you
check location usage in your app. -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- Needed only if your app makes the device discoverable to Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- Needed only if your app communicates with already-paired Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
...
</manifest>
定位权限
<!-- Allows an app to access approximate location. 近似定位权限,api1,如:网络定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Allows an app to access precise location 精准定位权限,api1,如:GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Allows an app to access location in the background. 后台定位权限,api29,android10新增 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
android6
Android 10 之前只有ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION;正常申请即可
android10
Android 10 新增加了后台定位权限:ACCESS_BACKGROUND_LOCATION,该权限对应始终允许;老的权限:ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION代表仅前台使用允许;
应用的targetSdkVersion<Q,谷歌提供了兼容性方案,只要应用申请了老的位置权限ACCESS_FINE_LOCATION或者ACCESS_COARSE_LOCATION,会默认请求ACCESS_BACKGROUND_LOCATION权限,动态授权弹框参考下面第一个图。
应用的TargetSdkVersion>=Q,如果应用必须要始终定位,可以只申请ACCESS_BACKGROUND_LOCATION即可,权限弹框参考下面第三个图;如果应用只需要申请前台定位,则只需要申请老的定位权限即可,具体授权弹框参考第二个图。如果都申请则出现三态权限弹框,参考下面第一个图。
android11
argetSdkVersion=android11 时,不能同时申请普通定位权限和后台定位权限。argetSdkVersion=android11,在android11手机上,同时申请两个权限将直接授权失败,不会有弹窗提示。
requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION), 100)
解决方案:只有成功申请了 普通定位权限,才能申请后台定位权限。
\1) 先申请前台位置信息访问权限;
\2) 再申请后台位置信息访问权限,引导用户到设置中进行授予。
android12
Android12新增模糊定位功能,使用户可以更加细粒度的管控设备位置权限。
需要注意的是,Android12要求必须同时请求ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION权限才会触发相关权限弹框,如果只请求ACCESS_COARSE_LOCATION权限,系统将只会弹出请求大致位置的弹框。如果开发者只请求ACCESS_FINE_LOCATION,系统会忽略该请求,并在Logcat里记录错误信息:ACCESS_FINE_LOCATION must be requested with ACCESS_COARSE_LOCATION(测试时直接申请确切位置权限也是可以的)。开发者在适配Android12应用时一定要注意上述权限请求相关要求。
在 Android 12 和更高版本中,用户可以转到系统设置,以设置任何应用的首选位置信息精确度,而不管该应用的目标 SDK 版本是什么。即使您的应用安装在搭载 Android 11 或更低版本的设备上,用户随后又将该设备升级到 Android 12 或更高版本,也是如此。
**注意: **如果用户从权限对话框或在系统设置中将应用的位置信息使用权从确切位置降级到大致位置,系统会重启应用的进程。
大致位置
提供设备位置的估算值,将范围限定在大约 1.6 公里(1 英里)内。当您声明 ACCESS_COARSE_LOCATION 权限(而非 ACCESS_FINE_LOCATION 权限)时,您的应用会使用这种级别的位置信息精确度。
确切位置
提供尽可能准确的设备位置估算值,通常将范围限定在大约 50 米(160 英尺)内,有时精确到几米(10 英尺)范围以内。当您声明 ACCESS_FINE_LOCATION 权限时,您的应用会使用这种级别的位置信息精确度。
如果用户授予大致位置信息权限,您的应用只能获取大致位置信息(无论它声明了哪些位置信息权限)。
当用户仅授予大致位置信息使用权时,您的应用应该仍会正常工作。如果应用中的某项功能确实需要使用 ACCESS_FINE_LOCATION 权限访问确切位置,您可以请求用户允许该应用获取确切位置信息。
存储权限
存储基本知识
先来看看存储区域划分:
其中,以下目录无需存储权限即可访问:
1、App自身的内部存储 2、App自身的自带外部存储-私有目录
剩下的都需要申请存储权限,Android 10.0前后对于存储作用域访问的区别就体现在如何访问剩余这些目录内的文件。
重点在自带外部存储之共享存储空间和其它目录
android6
申请危险权限,即可正常读写
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android10
分区存储,在Android10就已经推行了,简单的说,就是应用对于文件的读写只能在沙盒环境,也就是属于自己应用的目录里面读写。其他媒体文件可以通过MediaStore进行访问。
在targetSdkVersion = 29应用中,设置android:requestLegacyExternalStorage="true",就可以不启动分区存储,让以前的文件读取正常使用。
android11
分区存储强制执行
清单权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
从 Android 11 开始,应用无法在外部存储设备上创建自己的应用专用目录。如需访问系统为您的应用提供的目录,请调用 getExternalFilesDirs()。
公有共享目录
- Image: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
- Video: MediaStore.Video.Media.EXTERNAL_CONTENT_URI
- Audio: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
- Downloads: MediaStore.Downloads.EXTERNAL_CONTENT_URI
android11之后,不能使用File相关的api进行文件操作,android提供了MediaStore API进行文件的操作,一个 文本文件 , 只能存储在 Download 和 Documents 目录下 , Download 目录可以存放任何类型的文件 , Documents 目录只能存储文本文件 。
一个在downloads目录下文件存储操作:
创建文件并写入数据
val contentResolver = context.contentResolver
//Uri uri = MediaStore.Files.getContentUri("external");
val uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
val values = ContentValues()
val path = Environment.DIRECTORY_DOWNLOADS + "/device1"
values.put(MediaStore.Downloads.RELATIVE_PATH, path)
values.put(MediaStore.Downloads.DISPLAY_NAME, "devicesn1.text")
values.put(MediaStore.Downloads.TITLE, path)
val resultUri = contentResolver.insert(uri, values)
try {
val outputStream = contentResolver.openOutputStream(resultUri!!)
//将字符串转成字节
val contentInBytes = content.toByteArray()
outputStream!!.write(contentInBytes)
outputStream.flush()
outputStream.close()
Log.i(TAG, "插入sn内容成功: $content")
//Toast.makeText(MainActivity.this, "插入成功", Toast.LENGTH_LONG).show();
} catch (e: Exception) {
e.printStackTrace()
//失败
Log.e(TAG, "存储sn失败:" + e.message)
}
读数据
var content: String? = ""
val extnerl = MediaStore.Downloads.EXTERNAL_CONTENT_URI
Log.i(TAG, "Fileuri:$extnerl")
val selection = MediaStore.Downloads.DISPLAY_NAME + "=?"
val args = arrayOf("devicesn1.text")
val projections = arrayOf(MediaStore.Downloads._ID)
val cursor = context.contentResolver.query(extnerl, projections, selection, args, null)
if (cursor!!.moveToFirst()) {
val queryUir = ContentUris.withAppendedId(extnerl, cursor.getLong(0))
//Toast.makeText(MainActivity.this, "查询success" + queryUir, Toast.LENGTH_LONG).show();
Log.i(TAG, "查询success$queryUir")
cursor.close()
val contentResolver = context.contentResolver
var inputStream: InputStream? = null
try {
inputStream = contentResolver.openInputStream(queryUir)
val buffer = ByteArray(1024)
var len = 0
val bos = ByteArrayOutputStream()
while (inputStream!!.read(buffer).also { len = it } != -1) {
bos.write(buffer, 0, len)
}
bos.close()
val str = String(bos.toByteArray())
content = str
//tv_find_file.setText(str);
} catch (e: Exception) {
e.printStackTrace()
}
}
android13 细化的媒体权限
从Android 13开始,如果你的应用targetSdk指定到了33或以上,那么READ_EXTERNAL_STORAGE权限就完全失去了作用,申请它将不会产生任何的效果。
与此相对应地,Google新增了READ_MEDIA_IMAGES、READ_MEDIA_VIDEO和READ_MEDIA_AUDIO这3个运行时权限,分别用于管理手机的照片、视频和音频文件。
也就是说,以前只要申请一个READ_EXTERNAL_STORAGE权限就可以了。现在不行了,得按需申请,用户从而能够更加精细地了解你的应用到底申请了哪些媒体权限。
Android 14
为了能够更好地保护用户隐私, Google在Android 14系统中新增了选择性照片和视频访问授权功能。
那么什么是选择性照片和视频访问授权呢?
在过去,当一个App申请了READ_MEDIA_IMAGES权限,如果用户选择了同意,那么该App就可以访问这台手机上所有的照片。用户是没有办法限制该App只能访问特定的某几张照片的。
而Android 14新增的这个功能则允许用户选择,是一次性授权该App访问所有的照片,还是只能访问几张特定的照片。视频也是同样的道理。
Android 14推出了一个全新的运行时权限,也就是选择性照片和视频访问权限:READ_MEDIA_VISUAL_USER_SELECTED
android.permission.READ_MEDIA_VISUAL_USER_SELECTED