android6.0以前,我们把app需要用到的权限全部罗列在Manifest清单文件中。安装app时android系统会询问用户是否授予这些权限,拒绝后则无法安装app。如果授予,则安装app,之后无法修改授予状态。
android6.0将权限分为普通权限(不涉及用户隐私和安全)和危险权限(设计用户隐私和安全)。普通权限和andorid6.0之前一样,在Manifest清单文件中申请即可。危险权限需要在使用时动态申请,由用户决定是否授予。
危险权限分组:
自Android6.0起,HttpClient系列代码从SDK中剔除,推荐使用HttpURLConnection。
对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。
要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。
(1)在AndroidManifest.xml清单文件中注册provider
...
...
...
(2)res/xml中定义对外暴露的文件夹路径:
name:一个引用字符串。
path:文件夹“相对路径”,完整路径取决于当前的标签类型。
7.0之前跳转到系统相机拍照的代码,需要提前设定好保存图片的uri,跳转到相机应用
String cachePath = getApplicationContext().getExternalCacheDir().getPath();
File picFile = new File(cachePath, "test.jpg");
Uri picUri = Uri.fromFile(picFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, picUri);
startActivityForResult(intent, 100);
7.0之后:
String cachePath = getApplicationContext().getExternalCacheDir().getPath();
File picFile = new File(cachePath, "test.jpg");
Uri uriForFile = null;
if (Build.VERSION.SDK_INT >= 24) {
uriForFile = FileProvider.getUriForFile(MainActivity.this, "com.demo.fileprovider", picFile);
} else {
uriForFile = Uri.fromFile(picFile);
}
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriForFile);
startActivityForResult(intent, 100);
将Uri.fromFile(file)换成了FileProvider.getUriForFile,这里getUriForFile方法的第二个参数,就是第一步的provider的authorities
Android 7.0 移除了三项隐式广播。
面向 Android 7.0 开发的应用不会收到 CONNECTIVITY_ACTION 广播,即使它们已有清单条目来请求接受这些事件的通知。在主线程中通过Context.registerReceiver()动态注册了CONNECTIVITY_ACTION广播,该应用程序仍然可以接收到该广播。
应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 广播。此项优化会影响所有应用,而不仅仅是面向 Android 7.0 的应用。
Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。targeSdk升级到26之后,所有的通知的实现都需要提供通知渠道,如果不提供通知渠道的话,所有通知在8.0系统上面都不能正常展示。
//通知渠道id
val channelId = "channelId"
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//通知渠道名称
val channelName = "channelName"
//通知渠道重要程度
val importance = NotificationManager.IMPORTANCE_HIGH
//构建通知渠道
val channel = NotificationChannel(channelId, channelName, importance)
//设置通知渠道描述
channel.description = ""
//向系统注册通知渠道,注册后则不能修改重要性以及其他通知行为,但可以删除
notificationManager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, "channelId")
.setContentTitle("标题")
.setContentText("消息内容")
.setSmallIcon(R.drawable.ic_launcher_background)
.setAutoCancel(true)//点击自动消失
.build()
//通知id,每个通知都应该不同否则会覆盖
val notifyId = 1
notificationManager.notify(notifyId, notification)
如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。
8.0 的应用需要在 AndroidManifest.xml 中声明 REQUEST_INSTALL_PACKAGES 权限,否则将无法进行应用内升级。
我们自己定义的广播隐式调用不能接收,显式Intent可以接收。或者动态注册,然后隐式调用可以接收。
如果要接收系统广播只能动态注册。
在图标适配中有两个部分,前景图和背景图,所以设计的时候需要提供前景图和背景图两个图,这样在不同的厂商无论图标的背景怎么换都是对图标的背景图进行切割,而前景图是不会变的。
之前对于隐私权限只要申请一个就会将其在的权限组全部通过,android 8.0以后申请单个只给单个;
1、在 Android 9 中,不能从非 Activity 环境中启动 Activity,除非您传递 Intent 标志 FLAG_ACTIVITY_NEW_TASK。 如果您尝试在不传递此标志的情况下启动 Activity,则该 Activity 不会启动,系统会在日志中输出一则消息。
2、前台服务:针对 Android 9 或更高版本并使用前台服务的应用必须请求 FOREGROUND_SERVICE 权限。 这是普通权限,因此,系统会自动为请求权限的应用授予此权限。
3、默认情况下启用网络传输层安全协议 (TLS):如果应用以 Android 9 或更高版本为目标平台,则默认情况下 isCleartextTrafficPermitted() 函数返回 false。 如果您的应用需要为特定域名启用明文,您必须在应用的网络安全性配置中针对这些域名将 cleartextTrafficPermitted 显式设置为 true。
在 res 目录下新建xml文件夹,添加network_security_config.xml文件:
AndroidManifest.xml中的application添加:
...
4、对非 SDK 接口的限制:现已禁止访问特定的非 SDK 接口,无论是直接访问,还是通过 JNI 或反射进行间接访问。尝试访问受限制的接口时,会生成 NoSuchFieldException 和 NoSuchMethodException 之类的错误。
5、为改善 Android 9 中的应用稳定性和数据完整性,应用无法再让多个进程共用同一 WebView 数据目录。 此类数据目录一般存储 Cookie、HTTP 缓存以及其他与网络浏览有关的持久性和临时性存储。
如果开发者需要在多进程中使用 WebView,则必须先调用 WebView.setDataDirectorySuffix() 方法为每个进程设置用于存储 WebView 数据的目录。若多进程 WebView 之间需要共享数据,开发者需自己通过 IPC 的方式实现。
此外,若开发者只想在一个进程中使用 WebView,并且希望严格执行这个规则,可以通过在其他进程中调用 WebView.disableWebView() 方法,这样其他进程创建 WebView 实例就会抛出异常。
6、在 Android 9 中,Build.SERIAL 始终设置为 "UNKNOWN" 以保护用户的隐私。
如果您的应用需要访问设备的硬件序列号,您应改为请求 READ_PHONE_STATE 权限,然后调用 getSerial()。
分区存储将外部存储分成两部分:
(1)App-specific directory (沙盒目录)
APP只能在Context.getExternalFilesDir()目录下通过File的方式创建文件,APP卸载的时候,这个目录下的文件会被删除;无法通过File的方式在其他路径创建文件。
(2)Public Directory 公共目录
公共目录包括:多媒体公共目录(Photos, Images, Videos, Audio)和下载文件目录(Downloads)。
APP可以通过MediaStore或者SAF(System Access Framework)的方式访问其中的文件。APP卸载后,文件不会被删除。
Android Q以上移除了WRITE_EXTERNAL_STORAGE权限,应用不需要这个权限就可以向沙盒内存储文件,也可以通过媒体数据库的方式保存媒体数据至特定位置。
App卸载后,对应的沙盒目录也会被删除,如果APP想要在卸载时保留沙盒目录下的数据,要在AndroidManifest.xml中声明android:hasFragileUserData="true",这样在 APP卸载时就会有弹出框提示用户是否保留应用数据。
Android 10 引入了 ACCESS_BACKGROUND_LOCATION 权限(危险权限)。
该权限允许应用程序在后台访问位置。如果请求此权限,则还必须请求ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限。只请求此权限无效果。
Android 10中必须具有 ACCESS_FINE_LOCATION 权限才能使用的类和方法:
电话
TelephonyManager
getCellLocation()
getAllCellInfo()
requestNetworkScan()
requestCellInfoUpdate()
getAvailableNetworks()
getServiceState()
TelephonyScanManager
requestNetworkScan()
TelephonyScanManager.NetworkScanCallback
onResults()
PhoneStateListener
onCellLocationChanged()
onCellInfoChanged()
onServiceStateChanged()
WLAN
WifiManager
startScan()
getScanResults()
getConnectionInfo()
getConfiguredNetworks()
WifiAwareManager
WifiP2pManager
WifiRttManager
蓝牙
BluetoothAdapter
startDiscovery()
startLeScan()
startScan()
BluetoothAdapter.LeScanCallback
BluetoothLeScanner
应用处于后台时,无法启动Activity。
(1)手动适配---资源替换
res 下新建 values-night目录,创建对应的colors.xml文件。
(2)自动适配---Force Dark
应用必须选择启用 Force Dark,方法是在其主题背景中设置 android:forceDarkAllowed="true"。此属性会在所有系统及 AndroidX 提供的浅色主题背景(例如 Theme.Material.Light)上设置。
Force Dark需要注意几点:
如果使用的是 DayNight 或 Dark Theme 主题,则设置forceDarkAllowed 不生效。
如果有需要排除适配的部分,可以在对应的View上设置forceDarkAllowed为false。
对不可重置的设备标识符实施了限制
受影响的方法包括:
Build
getSerial()
TelephonyManager
getImei()
getDeviceId()
getMeid()
getSimSerialNumber()
getSubscriberId()
从 Android 10 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE 特许权限才能正常使用以上这些方法。
如果你的应用没有该权限,却仍然使用了以上的方法,则返回的结果会因目标 SDK 版本而异:
如果应用以 Android 10 或更高版本为目标平台,则会发生 SecurityException。
如果应用以 Android 9(API 级别 28)或更低版本为目标平台,则相应方法会返回 null 或占位符数据(如果应用具有 READ_PHONE_STATE 权限)。否则,会发生 SecurityException。
这项改动表示第三方应用无法获取Device ID这类唯一标识。
除非您的应用是默认输入法 (IME) 或是目前处于焦点的应用,否则它无法访问 Android 10 或更高版本平台上的剪贴板数据。
以 Android 10 或更高版本为目标平台的应用无法启用或停用 WLAN。WifiManager.setWifiEnabled()方法始终返回 false。
Android 11在分区存储基础上限制了应用访问其他应用的文件。
分区存储将存储空间分为两部分:
公共目录:Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等
公共目录的文件在App卸载后,不会删除
可以通过SAF、MediaStore接口访问
拥有权限,也能通过路径直接访问
应用专属目录
应用专属目录只能自己直接访问
App卸载,数据会清除。
将应用更新为以 Android 11 为目标平台后,您将无法使用requestLegacyExternalStorage,而且也没有其他标记可以提供停用分区存储。
所有文件访问权限MANAGE_EXTERNAL_STORAGE,用来获取所有文件的管理权限。
val intent = Intent()
intent.action= Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
startActivity(intent)
//判断是否获取MANAGE_EXTERNAL_STORAGE权限:
val isHasStoragePermission= Environment.isExternalStorageManager()
Android 11 更改了应用在读取电话号码时使用的与电话相关的权限。
如果应用以 Android 11 或更高版本为目标平台,并且需要访问以下列表中显示的电话号码 API,则必须请求 READ_PHONE_NUMBERS 权限,而不是 READ_PHONE_STATE 权限。
TelephonyManager 类和 TelecomManager 类中的 getLine1Number() 方法。
TelephonyManager 类中不受支持的 getMsisdn() 方法。
如果应用声明 READ_PHONE_STATE 以调用前面列表中的方法以外的方法,可以继续在所有 Android 版本中请求 READ_PHONE_STATE。不过,如果仅对前面列表中的方法使用 READ_PHONE_STATE 权限,请按以下方式更新您的清单文件:
更改 READ_PHONE_STATE 的声明,以使应用仅在 Android 10(API 级别 29)及更低版本中使用该权限。
添加 READ_PHONE_NUMBERS 权限。
出于安全方面的考虑,同时也为了保持良好的用户体验,如果包含自定义视图的消息框是以 Android 11 或更高版本为目标平台的应用从后台发送的,系统会屏蔽这些消息框。请注意,仍允许使用文本消息框;此类消息框是使用 Toast.makeText() 创建的,并不调用 setView()。
如果您的应用仍尝试从后台发布包含自定义视图的消息框,系统不会向用户显示相应的消息,而是会在 logcat 中记录以下消息:
W/NotificationService: Blocking custom toast from package \
due to package not in the foreground
消息框回调
如果您希望在消息框(文本消息框或自定义消息框)出现或消失时收到通知,请使用 Android 11 中添加的 addCallback() 方法。
文本消息框 API 变更
以 Android 11 或更高版本为目标平台的应用会发现文本消息框受到以下负面影响:
getView() 方法返回 null。
以下方法的返回值并不反映实际值,因此您不应在应用中依赖于它们:
getHorizontalMargin()
getVerticalMargin()
getGravity()
getXOffset()
getYOffset()
以下方法是空操作,因此您的应用不应使用它们:
setMargin()
setGravity()
从 Android 11 开始,只有预装的系统相机应用可以响应以下 intent 操作:
android.media.action.VIDEO_CAPTURE
android.media.action.IMAGE_CAPTURE
android.media.action.IMAGE_CAPTURE_SECURE
也就是说,如果我调用intent唤起照相机,使用VIDEO_CAPTURE的action,只有系统的相机能够响应,而第三方的相机应用不会响应了。
如果要使用特定的第三方相机应用来代表其捕获图片或视频,可以通过为intent设置软件包名称或组件来使这些intent变得明确。
Android 11 添加了在您的应用中支持 5G 的功能。
新的Android11也是支持了5G相关的一些功能,包括:
检测是否连接到了5G网络
检查按流量计费性
检测5G网络,通过TelephonyManager的监听方法:
private fun getNetworkType(){
val tManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
tManager.listen(object : PhoneStateListener() {
@RequiresApi(Build.VERSION_CODES.R)
override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
if (ActivityCompat.checkSelfPermission(this@Android11Test2Activity, android.Manifest.permission.READ_PHONE_STATE) != android.content.pm.PackageManager.PERMISSION_GRANTED) {
return
}
super.onDisplayInfoChanged(telephonyDisplayInfo)
when(telephonyDisplayInfo.networkType) {
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> showToast("高级专业版 LTE (5Ge)")
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> showToast("NR (5G) - 5G Sub-6 网络")
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> showToast("5G+/5G UW - 5G mmWave 网络")
else -> showToast("other")
}
}
}, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
}
检测流量计费方法,监听网络,在回调中判断:
val manager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
manager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities)
//true 代表连接不按流量计费
val isNotFlowPay=networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ||
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
}
})
在搭载 Android 11 的设备上,当应用中的某项功能请求在后台访问位置信息时,用户看到的系统对话框不再包含用于启用后台位置信息访问权限的按钮。如需启用后台位置信息访问权限,用户必须在设置页面上针对应用的位置权限设置一律允许选项。
Android 11 更改了应用查询用户已在设备上安装的其他应用以及与之交互的方式。使用
如果应用以 Android 11 或更高版本为目标平台,您可能需要在应用的清单文件中添加
从 Android 9 开始,应用仅限于在前台访问摄像头和麦克风。为了进一步保护用户,Android 11 更改了前台服务访问摄像头和麦克风相关数据的方式。如果您的应用以 Android 11 为目标平台并且在某项前台服务中访问这些类型的数据,您需要在该前台服务的声明的 foregroundServiceType 属性中添加新的 camera 和 microphone 类型。
应用某项前台服务需要访问位置信息、摄像头和麦克风,那么就要在清单文件中这样添加:
从 Android 12 开始,在搭载 Android 12 或更高版本的设备上运行时,所有应用都将拥有启动动画。这包括启动时的进入应用动作、显示应用图标的启动画面,以及向应用本身的过渡。
(1)应用图标应该是矢量可绘制对象(AVD XML),它可以是静态或动画形式。虽然动画的时长可以不受限制,但我们建议不超过1,000 毫秒。默认情况下,使用启动器图标。
(2)可以选择添加图标背景;在图标与窗口背景之间需要更高的对比度时图标背景很有用。如果您使用一个自适应图标,当该图标与窗口背景之间的对比度足够高时,就会显示其背景。
(3)与自适应图标一样,前景的三分之一被遮盖。
(4)窗口背景由不透明的单色组成。如果窗口背景已设置且为纯色,则未设置相应的属性时默认使用该背景。
以Android 12为目标平台的App,如果其包含的四大组件中使用到了Intent过滤器(intent-filter),则必须显式声明 android:exported 属性,否则App将无法在Android 12及更高系统版本的设备上安装。
如果您的应用请求ACCESS_COARSE_LOCATION但未请求ACCESS_FINE_LOCATION,则此变更不会影响您的应用。
如果您的应用请求ACCESS_FINE_LOCATION 运行时权限,您还应请求ACCESS_COARSE_LOCATION权限,以便处理用户授予应用大致位置访问权限的情形。
如果您的应用程序以Android 12为目标平台,您必须为应用创建的每个 PendingIntent 对象指定可变性。这项额外的要求可提高应用的安全性。因为三方app可以通过劫持PendingIntent,然后改写里面的action、category、data等,造成重定向攻击。
适配的具体就是在创建 PendingIntent时,使用 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 标志。否则运行时会报IllegalArgumentException。
如果您的应用程序面向Android 12或更高版本,使用蓝牙功能时请在应用程序的清单文件中声明以下权限:
BLUETOOTH_SCAN:允许蓝牙设备扫描。
BLUETOOTH_CONNECT:允许蓝牙设备连接。
BLUETOOTH_ADVERTISE:允许当前蓝牙设备可以被其他蓝牙设备发现。
对于以前的与蓝牙相关的权限声明,设置android:maxSdkVersion到30。此应用兼容性步骤可帮助系统仅授予您的应用在运行Android 12 的设备上安装时所需的蓝牙权限。
...
如果您的应用程序使用蓝牙扫描结果来获取物理位置,请声明ACCESS_FINE_LOCATION。以前版本中(6.0 ~ 11)是必须申请定位权限,才可以进行蓝牙扫描。
在Android 12上,如果你的应用程序不使用蓝牙扫描结果来获取物理位置。可以添加android:usesPermissionFlags属性到您的BLUETOOTH_SCAN权限声明,并将该属性的值设置为neverForLocation。
...
如果应用以 Android 13 或更高版本为目标平台,并且需要访问其他应用已经创建的媒体文件,必须请求以下一项或多项细化的媒体权限,而不是READ_EXTERNAL_STORAGE 权限:
媒体类型 |
请求权限 |
图片和照片 |
READ_MEDIA_IAMGES |
视频 |
READ_MEDIA_VIDEO |
音频文件 |
READ_MEDIA_AUDIO |
如果用户之前向您的应用授予了 READ_EXTERNAL_STORAGE 权限,系统会自动向您的应用授予细化的媒体权限。否则,当应用请求上表中显示的任何权限时,系统会显示面向用户的对话框。
...
在AndroidManifest.xml中对发送通知权限进行声明:
...
POST_NOTIFICATIONS权限只有在应用程序的targetSdk指定成33或更高时才会有用。
要确认用户是否已启用通知,请调用 areNotificationsEnabled()。
如果用户拒绝授予通知权限,就不会在抽屉式通知栏中看到与前台服务相关的通知。 不过,无论是否授予通知权限,用户都会在前台服务 (FGS) 任务管理器中看到与前台服务相关的通知。
4、WebView
从Android 13开始,以Android13(API 33+)为目标平台的应用,WebView存在以下方法与API调整:
WebSettings.setAppCacheEnabled() 方法废弃。
WebSettings.setForceDark() 方法废弃。
从Android 13开始,以Android13(API 33+)为目标平台的应用,注册静态广播时,需设置对其他应用的可见性:
若对其他应用可见,广播注册时设置:Context.RECEIVER_EXPORTED
若仅应用内使用,广播注册时设置:Context.RECEIVER_NOT_EXPORTED
private void registerTestReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction("com.xiaxl.test.action");
// api >= 33
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// 跨应用间使用
MainActivity.this.registerReceiver(mTestReceiver, filter, Context.RECEIVER_EXPORTED);
// 应用内使用
//MainActivity.this.registerReceiver(mTestReceiver, filter, Context.RECEIVER_EXPORTED);
}
// api <= 32
else {
MainActivity.this.registerReceiver(mTestReceiver, filter);
}
}
目前该增强措施并非默认生效,开发者需启用 DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED兼容性框架,并在动态注册广播时指定是否接受其他应用的广播。
如果启用了 DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED 兼容性框架更改,则必须为每个广播接收器指定 RECEIVER_EXPORTED 或 RECEIVER_NOT_EXPORTED。否则,当您尝试注册广播接收器时,系统会抛出 SecurityException。
在以前的 Android 版本中,用户需要向您的应用授予 在以前的 Android 版本中,用户需要向您的应用授予 ACCESS_FINE_LOCATION 权限,应用才能完成一些常见的 Wi-Fi 用例。
由于用户很难将位置信息权限与 Wi-Fi 功能相关联,因此 Android 13(API 级别 33)在 NEARBY_DEVICES 权限组中引入了运行时权限,适用于管理设备与附近 Wi-Fi 接入点连接情况的应用。此权限 (NEARBY_WIFI_DEVICES) 可满足以下 Wi-Fi 用例:
查找或连接到附近的设备,如打印机或媒体投射设备。通过该工作流,您的应用可以完成以下类型的任务:
通过带外方式(例如通过 BLE)接收 AP 信息。
使用仅限本地使用的热点,通过 Wi-Fi 感知和连接功能发现并连接到设备。
通过 Wi-Fi 直连发现和连接到设备。
发起与已知 SSID(例如汽车或智能家居设备)的连接。
开启仅限本地使用的热点。
连接到附近的 Wi-Fi 感知设备。
Android 13 中引入了“在使用时”访问身体传感器(例如心率、体温和血氧饱和度)的概念。
如果您的应用以 Android 13 为目标平台,并且在后台运行时需要访问身体传感器信息,那么除了现有的 BODY_SENSORS 权限外,您还必须声明新的 BODY_SENSORS_BACKGROUND 权限。