不同版本特性
版本 | 特性 |
---|---|
6.0 | 动态权限 |
7.0 | 1、应用间共享文件 禁止使用 file://URI ,否则抛出 FileUriExposedException 异常 在应用间共享文件,需要通过 content://URI 并授予 URI 临时访问权限 实现: 自定义 MyFileProvider extends FileProvider 再 manifest 中 注册该 在 2、Apk signature scheme v2 V1签名不会校验 apk文件的注释区 V2签名会校验 apk文件 注释区的是否修改 V2签名增加一个签名区块,签名区块中可根据固定的 id,获取签名信息 签名区块不会校验是否修改 3、Toast导致的BadTokenException 7.1版本(API25),Toast 的 WindowManager.LayoutParams参数新增了一个token属性, 用于对添加的窗口进行校验 UI线程发生阻塞导致 TN.show() 没有及时执行时,NMS 检测超时后会删除 WMS中的该token,即造成token失效。 解决方案:反射获取 mTN # mHandler,通过静态代理,在 mHandler 发送消息时加上 try-catch 4、Android 7.0 WebView 部分机型打不开 通过重写 onReceivedSslError() 过滤掉 部分错误 5、多语言特性 增加 LocaleList.getDefault() 获取多语言列表 多语言查找规则: 查找系统设置的第一语言(比如,中文简体)最匹配的资源 没找到则查找第一语言所属语言(比如,通用中文)匹配的资源 没找到则查找第一语言的其他分支语言(比如,中文简体-香港)匹配的资源 如果第一语言的资源都没有找到,查看是否设置多个语言 如果有,查看是否有与第二语言(比如,英国英语)最匹配的资源 如果没有查找第二语言所属的语言(通用英语)匹配的资源 没有则查找第二语言其他分支(美国英语)匹配的资源 如果没有,则查找第三语言、第四语言…的资源 如果都没有则加载默认资源 values/string.xml 如果默认资源也没有就抛异常 6、通知栏适配 1)增加 Notification.DecoratedCustomViewStyle()、Notification.DecoratedMediaCustomViewStyle(), 帮助更好的装饰带有 RemoteViews 的通知栏消息 2)需要动态设置 Builder.setShowWhen(true) 才会显示时间 3)支持 Action的直接回复,通过 RemoteInput 实现,且回复的消息内容支持立即添加到通知栏 4)支持通知消息组,相似的消息在达到一定数量后会按照消息组来显示 5)增加 NotificationManager.areNotificationsEnabled 获取是否开启了通知权限 7、后台优化 删除了三项隐式广播 CONNECTIVITY_ACTION、ACTION_NEW_PICTURE、ACTION_NEW_VIDEO 避免隐式广播在后台频繁启动监听了这些广播的应用,带来很大的电量消耗 应对策略: JobScheduler API 后系统控制后台任务执行时机 8、PopupWindow位置不正确 1)使用了update方法,同时设置了Gravity update方法在 7.0 以上会获取 Gravity 进行更新判断 解决方法:不使用update方法 2)PopupWindow 高度为 MATCH_PARENT,调用 showAsLocation 显示时, PopupWindow 没有在指定控件的下方显示。如果使用showAsDropDown,会全屏显示。 解决方法: a:指定 PopupWindow 的高度为 WRAP_CONTENT,调用 showAsDropDown 方法展示 b:弹出前,如果是 7.0以上,计算并设置 PopupWindow高度,再调用 showAtLocation 展示 |
8.0 | 1、运行时权限 6.0 之后,8.0 之前,应用运行时请求权限且被授予该权限 系统会错误地将 在清单中注册的、同一权限组的、权限 一起授予应用 (同一权限组的其他权限,用户未申请,也会被授予) 8.0 之后,系统只会授予应用明确请求的权限 (同一权限组的其他权限,用户未申请,不授予。用户申请,则自动授予) 按权限组来申请权限 2、通知适配 8.0 引入通知渠道,需要给通知创建渠道 用户界面将通知渠道称之为通知类别 3、悬浮窗适配 使用 SYSTEM_ALERT_WINDOW 权限的应用, 无法再使用以下窗口类型添加悬浮窗: TYPE_PHONE TYPE_PRIORITY_PHONE TYPE_SYSTEM_ALERT TYPE_SYSTEM_OVERLAY TYPE_SYSTEM_ERROR 必须使用 TYPE_APPLICATION_OVERLAY 的窗口类型 4、安装apk 8.0 去除了“允许未知来源” 选项 App有安装App的功能(检查更新之类的)无法正常安装。 解决方案: a:首先在 AndroidManifest 中 添加安装未知来源应用的权限: 系统会自动询问用户完成授权 b:先使用 canRequestPackageInstalls() 查询是否有此权限 如果没有,则使用 Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES 这个action 将用户引导至安装未知应用权限界面去授权 权限组、悬浮窗、安装未知来源应用的权限适配,可以使用 AndPermission 更加便捷 5、透明主题的 Activity 8.0 出现的bug(官方已经在8.1修复) 只有全屏不透明的activity才可以设置方向。 否则报错: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation 解决办法: a:去掉 透明 activity 的 b:去除透明效果 方案b最好是添加 values-v26目录,单独处理8.0 版本 6、集合的处理 8.0以上 AbstractCollection.removeAll(null) 和 AbstractCollection.retainAll(null) 始终引发 NullPointerException; 在之前,当集合为空时不会引发 NullPointerException。所以我们需要做判空处理。 7、后台执行限制 应用在两个方面受到限制: 后台服务限制: 处于空闲状态时,应用可以使用的后台服务存在限制 前台服务受限制 广播限制: 除了有限的例外情况,无法使用清单注册隐式广播 它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。 解决方案: 使用 JobScheduler 克服这些限制 JobScheduler 让应用安排为在未活跃运行时执行工作 关于JobScheduler的用法可以参考官方例子:android-JobScheduler 后台任务google推荐方案使用 WorkManager, WorkManager 内部维护着JobScheduler,自动维护后台任务, 同时满足后台Service和静态广播 在6.0 以下系统版本会自动切换为 AlarmManager 8、后台位置信息限制 为降低耗电量,8.0(API 26)对后台应用获取用户当前位置信息的频率进行限制 应用每小时仅接收几次位置信息更新 前台应用(满足以下任一条件): a:有可见的 Activity,无论 Activity 处于启动还是暂停状态。 b:有前台服务。 c:另一个前台应用已关联到该应用 (不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序) 解决方案: 将应用转至前台 调用 startForegroundService() 以启动应用中的某个前台服务 前台服务会以通知的形式持续显示在通知区域 |
9.0 | 1、新特性 利用 Wi-Fi RTT 进行室内定位 刘海屏 API支持 通知栏功能增强 多摄像头支持、摄像头更新 HDR VP9 视频、HEIF 图像压缩、Media API 2、non-SDK接口的使用 Android P 无法通过反射、JNI 间接使用非 SDK 接口 3、挖孔屏适配 有状态栏的页面,不会受到挖孔屏特性的影响; 全屏显示的页面,系统挖孔屏方案会下移应用界面避开挖孔区显示 P版本三方适配挖孔屏方案: a:新增挖孔屏挖孔尺寸和位置接口 WindowInsets#getDisplayCutout(); b:新窗口布局模式,可获取是否在挖孔区域布局 WindowManager.LayoutParams#layoutInDisplayCutoutMode 值说明: a)LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT: 默认模式,全屏窗口不使用挖孔区域,非全屏窗口正常使用挖孔区域。 b)LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS: 声明使用挖孔区域 c)LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER: 声明不使用挖孔区域 4、Battery Improvements 功耗解决方案 AAB(Auto Awesome Battery): i、通过ML算法将应用进行分类,不同类型的应用功耗管控策略不一样 ii、 Firebase Cloud Messaging (FCM):管控三方消息接收的频率 iii、应用的管控方法:Forced App Standby (FAS),谷歌不会通过清理应用来优化功耗 Extreme Battery Saver(EBS)谷歌超级省电模式; Smart screen brightness:屏幕亮度调节优化算法。 影响: 谷歌功耗方案对三方应用各种管控,存在导致应用后台功能无法正常使用的可能 5、不允许共享WebView数据目录 不能再跨进程共享单个WebView数据目录,否则会崩溃 6、移除对 Build.serial 的直接访问 需要 Build.serial 标识符 必须请求 READ_PHONE_STATE 权限然后使用 Android P 新增的新 Build.getSerial() 函数 7、SELinux 禁止访问应用的数据目录 不允许直接通过路径访问其他应用的数据目录 可以使用进程间通信 (IPC) 机制(包括通过传递 FD)共享数据 8、常见问题: 1)java.lang.SecurityException: Failed to find provider null for user 0; 问题原因:项目使用了ActiveAndroid,在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时 调用 ContentResolver # notifyChange 方法通知数据更新, 或调用 ContentResolver # registerContentObserver 方法监听数据变化 解决方案: 在清单文件配置 android:authorities=“com.ylmf.androidclient” android:enabled=“true” android:exported=“false”> 2)onError : CLEARTEXT communication to life.115.com not permitted by network security policy 问题原因:在Android P(API 28)系统的设备上或 targetSdkVersion 为28,默认要求使用加密连接 比如,默认只能使用HTTPS进行通信,若强行使用HTTP通信,则会得到上面的异常 解决方案: 在资源文件新建xml目录,新建文件 清单文件的application标签中添加配置: android:networkSecurityConfig="@xml/network_security_config" |
android Q | 1、定义公共媒体集合:Photos & Videos、Music、 Downloads 操作其他应用已创建的文件,必须首先请求相应的权限 Photos:READ_MEDIA_IMAGES 权限 Videos:READ_MEDIA_VIDEO 权限 Music:READ_MEDIA_AUDIO 权限 Downloads:需要让用户使用系统的文件选择器选择文件 2、访问共享集合通过 MediaStore API MediaStore.Images、MediaStore.Video、MediaStore.Audio、MediaStore.Downloads 3、外存中私有目录权限变更 获取应用私有目录文件,不再需要权限 4、新增 ACCESS_MEDIA_LOCATION 权限 一些照片在其数据中会包含位置信息 6,存储新特性 识别特定的外部存储设备 Android Q 为每个外部存储设备提供唯一的卷名 //获取卷名方式 Set volumeNames = MediaStore.getAllVolumeNames(context); 7,Activity后台活动限制 至少满足以下一个条件才能启动Activity: 1)该APP具有可见窗口,如,前台Activity 2)前台的另一个 APP 发送该应用程序的 PendingIntent 3)系统发送该 APP 的PendingIntent,如点击通知 4)系统向应用程序发送广播,如,SECRET_CODE_ACTION 只有应用程序预期启动UI的特定广播才免除8.Activity后台活动限制的兼容性 特殊情况如:来电、警报,需要立刻启动 Activity, 可以通过创建高优先级的通知,并提供 fullscreen intent 9.设备位置权限的访问控制 请求地理位置时,会通过对话框的形式给用户进行授权提示 此对话框允许用户授予对两个不同范围的位置访问权限: 在使用中(仅限前台)或始终(前台和后台) 新增权限 ACCESS_BACKGROUND_LOCATION Android Q 需要在后台运行时访问用户的位置,必须在应用的清单文件中声明该权限 10.位置限制的兼容性 Android Q 设备运行,但targetSdk < Q,则会出现以下行为: 系统会在安装期间自动添加 ACCESS_BACKGROUND_LOCATION 权限 在请求定位权限时,系统自动增加对 ACCESS_BACKGROUND_LOCATION 权限的申请 用户可以选择只在前台获取位置 11,允许应用程序降级 可以回滚到旧版本 12.对数据和标识符的更改 1)获取联系人信息,结果不再按交互频率排序 2)MAC地址随机化,默认传输随机的MAC 地址 获取随机MAC地址I:WifiConfiguration.getRandomizedMacAddress() 获取实际硬件MAC地址:WifiInfo.getFactoryMacAddress() 3)唯一标识符 需要 READ_PRIVILEGED_PHONE_STATE 权限,才能访问设备的不可重置标识符,包括 IMEI、序列号 4)访问剪贴板数据 除默认输入法程序外,没有焦点的应用无法访问剪贴板数据 5)访问USB串行需要用户许可 targetSdk >= Android Q,只能在用户授予您访问USB设备权限后才能读取序列号 6)相机和连接相关更改 访问相机信息需要获得许可 Android Q更改了 getCameraCharacteristics()方法返回的信息的广度必须具有 CAMERA 权限 才能访问 getCameraCharacteristics()方法的返回值中包含的设备元数据 7)启用、禁用Wi-Fi的限制 无法启用、停用Wi-Fi WifiManager.setWifiEnabled() 方法始终返回false 只能使用设置面板提示用户启用、禁用Wi-Fi |
//TODO android 10、折叠屏、刘海屏、环绕屏 适配
动态权限
1、应用间共享文件
2、Apk signature scheme v2
3、PopupWindow位置不正确
1)我们使用了update方法,同时设置了Gravity(Gravity.NO_GRAVITY没事)。
因为在update方法中有调用computeGravity方法去获取Gravity。
(7.0 以下没有获取Gravity进行更新判断)
解决方法:不使用update方法。
2)PopupWindow 高度为 MATCH_PARENT,在显示的时候调用showAsLocation方法时,
PopupWindow并没有在指定控件的下方显示。如果使用showAsDropDown,会全屏显示。
解决方法:
a:指定 PopupWindow 的高度为 WRAP_CONTENT, 调用 showAsDropDown方法。
b:弹出时做一下判断处理
// Android 7.x中,PopupWindow高度为match_parent时,会出现兼容性问题,需要处理兼容性
if (Build.VERSION.SDK_INT >= 24) {
int[] location = new int[2]; // 记录anchor在屏幕中的位置
anchor.getLocationOnScreen(location);
int offsetY = location[1] + anchor.getHeight();
// Android 7.1 ,8.0中,PopupWindow高度为 match_parent 时,会占据整个屏幕
if (Build.VERSION.SDK_INT >= 25) {
// 故而需要在 Android 7.1上再做特殊处理
// 获取屏幕高度
int screenHeight = ScreenUtils.getScreenHeight(context);
// 重新设置 PopupWindow 的高度
popupWindow.setHeight(screenHeight - offsetY);
}
popupWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, 0, offsetY);
} else {
popupWindow.showAsDropDown(anchor);
}
4、后台优化
一些隐式广播可以做一些特定的功能,如,当手机网络变成WiFi时自动下载更新包等。
但,这些隐式广播会在后台频繁启动已注册监听这些广播的应用,从而带来很大的电量消耗。
为了提升设备性能和用户体验,在Android 7.0 中删除了三项隐式广播,
以帮助优化内存使用和电量消耗
a:在 Android 7.0上 应用不会收到 CONNECTIVITY_ACTION 广播,
即使你在manifest清单文件中设置了请求接受这些事件的通知。
在前台运行的应用如果使用BroadcastReceiver 请求接收通知,
仍可以在主线程中侦听 CONNECTIVITY_CHANGE
b:在 Android 7.0上应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 类型的广播。
应对策略:
JobScheduler API
提供了一个稳健可靠的机制来安排满足指定条件(例如连入无线流量网络)
时所执行的网络操作。
您甚至可以使用 JobScheduler API 来适应内容提供程序变化。
5、多语言特性
6、通知栏适配
增加了Notification.DecoratedCustomViewStyle()
和Notification.DecoratedMediaCustomViewStyle(),
帮助更好的装饰带有RemoteViews的通知栏消息。
需要动态设置Builder.setShowWhen(true)才会显示时间。
支持Action的直接回复,通过RemoteInput实现,且回复的消息内容支持立即添加到通知栏。
支持通知消息组,相似的消息在达到一定数量后会按照消息组来显示。
增加了NotificationManager.areNotificationsEnabled告知应用是否开启了通知权限
7、WebView 问题
Android 7.0 WebView 部分机型打不开
通过重写 onReceivedSslError 过滤掉 部分错误
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if (error.getPrimaryError() == SslError.SSL_DATE_INVALID
|| error.getPrimaryError() == SslError.SSL_EXPIRED
|| error.getPrimaryError() == SslError.SSL_INVALID
|| error.getPrimaryError() == SslError.SSL_UNTRUSTED) {
handler.proceed();
} else {
handler.cancel();
}
super.onReceivedSslError(view, handler, error);
}
8、Toast导致的BadTokenException
1、运行时权限
6.0 之后,8.0 之前,如果应用在运行时请求权限并且被授予该权限,
系统会错误地将【属于同一权限组】并且在清单中注册的其他权限也一起授予应用。
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,则系统会立即授予该权限,而不会提示用户。
按权限组来申请权限。
2、通知适配
Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。
用户界面将通知渠道称之为通知类别。
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
//分组(可选)
//groupId要唯一
String groupId = "group_001";
NotificationChannelGroup group = new NotificationChannelGroup(groupId, "广告");
//创建group
notificationManager.createNotificationChannelGroup(group);
//channelId要唯一
String channelId = "channel_001";
NotificationChannel adChannel = new NotificationChannel(channelId,
"推广信息", NotificationManager.IMPORTANCE_DEFAULT);
//补充channel的含义(可选)
adChannel.setDescription("推广信息");
//将渠道添加进组(先创建组才能添加)
adChannel.setGroup(groupId);
//创建channel
notificationManager.createNotificationChannel(adChannel);
//创建通知时,标记你的渠道id
Notification notification = new Notification.Builder(MainActivity.this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentTitle("一条新通知")
.setContentText("这是一条测试消息")
.setAutoCancel(true)
.build();
notificationManager.notify(1, notification);
}
}
//8.0删除通知渠道
private void deleteNotificationChannel(String channelId){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.deleteNotificationChannel(channelId);
}
}
3、悬浮窗适配
使用 SYSTEM_ALERT_WINDOW 权限的应用,无法再使用以下窗口类型
来在其他应用和系统窗口上方显示提醒窗口:
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
应用必须使用名为 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
}
4、安装apk
Android 8.0 去除了“允许未知来源”选项,所以如果我们的App有安装App的功能(检查更新之类的),
那么会无法正常安装。
解决方案:
a:首先在AndroidManifest文件中添加安装未知来源应用的权限:
这样系统会自动询问用户完成授权。
b:可以先使用 canRequestPackageInstalls()查询是否有此权限,
如果没有的话使用 Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES 这个action
将用户引导至安装未知应用权限界面去授权。
private static final int REQUEST_CODE_UNKNOWN_APP = 100;
private void installAPK(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
if (hasInstallPermission) {
//安装应用
} else {
//跳转至“安装未知应用”权限界面,引导用户开启权限
Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
startActivityForResult(intent, REQUEST_CODE_UNKNOWN_APP);
}
}else {
//安装应用
}
}
//接收“安装未知应用”权限的开启结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_UNKNOWN_APP) {
installAPK();
}
}
对于权限组、悬浮窗还有安装未知来源应用的权限适配,
我们也可以使用 AndPermission 更加便捷。
5、透明主题的Activity
在targetSdk = 27,Android为8.0 的手机时,出现的bug(因为官方已经在8.1修复)
只有全屏不透明的activity才可以设置方向。否则报错如下:
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
…
解决办法:
a:去掉对应activity中的 screenOrientation 属性,或者对应设置方向的代码。
b:舍弃透明效果,在它的Theme中添加:
false
方案b最好是添加 values-v26目录,单独处理8.0 版本。个人推荐方案a
6、集合的处理
现在,AbstractCollection.removeAll(null) 和 AbstractCollection.retainAll(null)
始终引发 NullPointerException;
之前,当集合为空时不会引发 NullPointerException。所以我们需要做判空处理。
7、后台执行限制
应用在两个方面受到限制:
后台服务限制:
处于空闲状态时,应用可以使用的后台服务存在限制。
这些限制不适用于前台服务,因为前台服务更容易引起用户注意。
广播限制:
除了有限的例外情况,应用无法使用清单注册隐式广播。
它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。
在大多数情况下,应用都可以使用 JobScheduler 克服这些限制。
这种方式让应用安排为在未活跃运行时执行工作,
不过仍能够使系统可以在不影响用户体验的情况下安排这些作业。
关于JobScheduler的用法可以参考官方例子:android-JobScheduler
后台任务google推荐方案使用 WorkManager,
WorkManager可以自动维护后台任务,同时可适应不同的条件,
同时满足后台Service和静态广播,内部维护着JobScheduler,
而在6.0 以下系统版本则可自动切换为 AlarmManager
8)后台位置信息限制
为降低耗电量,Android 8.0(API 级别 26) 会对后台应用检索用户当前位置信息的频率进行限制。
应用每小时仅接收几次位置信息更新。
无论应用的目标 SDK 版本为何,
这些限制适用于在搭载 Android 8.0(API 级别 26) 或更高版本的设备上使用的所有应用。
前台应用(满足以下任一条件):
a:它具有可见的 Activity,无论 Activity 处于启动还是暂停状态。
b:它具有前台服务。
c:另一个前台应用已关联到该应用
(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)
解决方案:
将您的应用转至前台。
调用 startForegroundService() 以启动应用中的某个前台服务。
此前台服务处于活动状态后,会以通知的形式持续显示在通知区域。
1、新特性:
利用 Wi-Fi RTT 进行室内定位
刘海屏 API支持
通知栏功能增强
多摄像头支持和摄像头更新
HDR VP9 视频、HEIF 图像压缩和 Media API
2、non-SDK接口的使用
Android P 引入了针对非 SDK 接口的新使用限制,无论是直接使用还是通过反射或 JNI 间接使用。
无论应用是引用非 SDK 接口还是尝试使用反射或 JNI 获取其句柄,均适用这些限制。
3、挖孔屏适配
谷歌P版本提供了统一的挖孔屏方案和三方适配挖孔屏方案:
对于有状态栏的页面,不会受到挖孔屏特性的影响;
全屏显示的页面,系统挖孔屏方案会对应用界面做下移避开挖孔区显示;
已经适配的P的应用的全屏页面可以通过谷歌提供的适配方案使用挖孔区,真正做到全屏显示
P版本三方适配挖孔屏方案:
a:新增挖孔屏挖孔尺寸和位置接口
class WindowInsets {
DisplayCutout getDisplayCutout();
}
class DisplayCutout {
int getSafeInsetLeft();
int getSafeInsetTop();
int getSafeInsetRight();
int getSafeInsetBottom();
Region getBounds();
}
b:新窗口布局模式,允许应用程序请求是否在挖孔区域布局
class WindowManager.LayoutParams {
int layoutInDisplayCutoutMode;
final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
}
layoutInDisplayCutoutMode值说明:
a)LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
默认情况下,全屏窗口不会使用到挖孔区域,非全屏窗口可正常使用挖孔区域。
b)LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:
窗口声明使用挖孔区域
c)LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
窗口声明不使用挖孔区域
4、Battery Improvements 功耗解决方案
AAB(Auto Awesome Battery):
i、通过ML算法将应用进行分类,不同类型的应用功耗管控策略不一样
ii、 Firebase Cloud Messaging (FCM):管控三方消息接收的频率
iii、谷歌提供了统一的应用的管控方法:Forced App Standby (FAS),
谷歌不会通过清理应用来优化功耗
Extreme Battery Saver(EBS)谷歌超级省电模式;
Smart screen brightness:屏幕亮度调节优化算法。
影响:
谷歌功耗方案对三方应用各种管控,存在导致应用后台功能无法正常使用的可能。
5、不允许共享WebView数据目录
不能再跨进程共享单个WebView数据目录。
如果您的应用有多个使用WebView,CookieManager 或 android.webkit 包中的其他API的进程,
则当第二个进程调用WebView方法时,您的应用将崩溃。
6、移除对 Build.serial 的直接访问
需要 Build.serial 标识符的应用必须请求 READ_PHONE_STATE 权限,
然后使用 Android P 中新增的新 Build.getSerial() 函数
7、SELinux 禁止访问应用的数据目录
系统强制每个应用的 SELinux 沙盒对每个应用的私有数据目录
强制执行逐个应用的 SELinux 限制。
现在,不允许直接通过路径访问其他应用的数据目录。
应用可以继续使用进程间通信 (IPC) 机制(包括通过传递 FD)共享数据
8、常见问题:
1)java.lang.SecurityException: Failed to find provider null for user 0; …
问题原因:项目使用了ActiveAndroid,在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时,
调用 ContentResolver 的 notifyChange 方法通知数据更新,
或者调用 ContentResolver 的 registerContentObserver 方法监听数据变化时,会出现上述异常。
解决方案:
在清单文件配置
2)onError : CLEARTEXT communication to life.115.com not permitted by network security policy
问题原因:在Android P(API 28)系统的设备上或 targetSdkVersion 为28,默认要求使用加密连接。
比如,默认只能使用HTTPS进行通信,若强行使用HTTP通信,则会得到上面的异常。
解决方案:
在资源文件新建xml目录,新建文件
清单文件的application标签中添加配置:
android:networkSecurityConfig="@xml/network_security_config"
但还是建议都使用https进行传输
3)8.0,静态广播无法正常接收
问题原因: Android 8.0 引入了新的广播接收器限制,
因此您应该移除所有为隐式广播 Intent 注册的广播接收器
解决方案:
使用动态广播代替静态广播
4)Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
问题原因: Android 8.0 非全屏透明页面不允许设置方向
解决方案:android:windowIsTranslucent设置为false
为了提升设备性能和用户体验,在Android 7.0 中删除了三项隐式广播,
以帮助优化内存使用和电量消耗
a:在 Android 7.0上 应用不会收到 CONNECTIVITY_ACTION 广播,
即使你在manifest清单文件中设置了请求接受这些事件的通知。
在前台运行的应用如果使用BroadcastReceiver 请求接收通知,
仍可以在主线程中侦听 CONNECTIVITY_CHANGE
b:在 Android 7.0上应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 类型的广播。
除了有限的例外情况,应用无法使用静态注册隐式广播。
它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。
转载
6.0 以下的权限及在安装的时候,根据权限声明产生一个权限列表,
用户只有在同意之后才能完成app的安装。
造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)
6.0 以后运行时权限,保护了用户的隐私。
Google将权限分为两类,
一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的。
比如手机震动、访问网络等;
另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权。
比如读取sdcard、访问通讯录等。
可以通过 adb shell pm list permissions -d -g 进行查看。
如果app已被用户授权了同一组的某个危险权限,系统会立即授权,不需要用户去点击授权。
比如 app对 READ_CONTACTS 已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。
此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限
(ps:这个dialog是不能进行定制的)。
对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策,
禁止在您的应用外部公开 file://URI
如果一项包含文件 URI 的 intent 离开您的应用,则抛出 FileUriExposedException 异常。
要在应用间共享文件,您应发送一项 content://URI 并授予 URI 临时访问权限。
授权的最简单方式是使用 FileProvider 类。
转载
//不用实现任何方法
public class MyFileProvider extends FileProvider{
}
我们是否可以将export设置为true呢?很遗憾是不能的。
在FileProvider的内部:
@Override
public void attachInfo(Context context, ProviderInfo info) {
super.attachInfo(context, info);
// 抛异常
if (info.exported) {
throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
throw new SecurityException("Provider must grant uri permissions");
}
mStrategy = getPathStrategy(context, info.authority);
}
//path:要访问的路径
//name:名称,生成uri时使用
files-path 指的是应用程序内部存储的目录,
path:是文件的路径 .是根路径 字符串则是代表文件夹
比如:path=“aaa” 那么就代表的是该路径下的aaa文件夹
name:用于生成 Uri
比如,我们要获取该目录下名为 default_image.jpg 的文件。
生成的uri为:
content://com.example.myapp.fileprovider/myimages/default_image.jpg
图片的目录为:
/data/data/应用包名/files/images/default_image.jpg
调用系统相册:
File imagePath = new File(Context.getFilesDir(), "images");
File requestFile = new File(imagePath, "default_image.jpg");
//构造uri
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= 24) {
fileUri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", requestFile);
} else {
fileUri = Uri.fromFile(file);
}
private Intent requestFileIntent = new Intent(Intent.ACTION_VIEW);
requestFileIntent.setType("image/jpg");
//授予权限
requestFileIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//保存位置
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(requestFileIntent, 0);
1)说明
path: 目录名称,不能为空,可以为“.”
name: 生成uri需要,不能为空
指定目录的目录下。
//文件保存路径为:/storage/emulated/0/Android/data/应用包名/cache/update/default_image.jpg
path="update"
/>
2)对应关系
代表 context.getFileDir()
代表 context.getCacheDir()
代表 Environment.getExternalStorageDirectory()
代表 context.getExternalFilesDir()
代表 getExternalCacheDir()
//这个目录,只在 API 21+ 的设备存在
代表 Context.getExternalMediaDirs()