Android 8.0适配指北
本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发。
Android适配系列:
Android 6.0 的动态权限管理
Android 7.0脱坑指南
Android 8.0适配指北
Android 9.0 适配指南
又到了7月,这是我一年一度的写适配文章的时间,今年到了安卓8.0。为什么我会总会选在这个时间点,因为国内的手机升级总是慢一拍,还有好多的新特性可能也不支持。所以在没有真机的情况下,这类的适配也只是面向模拟器的适配,感觉只能是个参考,并不敢直接用到自己的项目上。不过这几年升级跟进速度是越来越快了,大概从去年底到五月基本都已经吃上奥利奥了。所以使用8.0的用户也越来越多了,适配的时机也就来了。好吧,结束这段瞎掰。。。
适配是个循序渐进的事情,不要想着一步到位。所以如果你还没有适配6.0或7.0可以先看看我之前的两篇了解一下。
1.谈谈Android 6.0 的动态权限管理
2.Android 7.0脱坑指南
1.准备工作
将我们项目中的targetSdkVersion
改为 26(8.0) 或者 27(8.1),记住不要超过27,毕竟我还没有告诉你Android P怎么适配(滑稽)。
2.运行时权限
首先引用官方的原文。
在 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
,则系统会立即授予该权限,而不会提示用户。
所谓权限组如下:
所以总结下来,如果你之前是用什么权限就去申请什么权限,那么恭喜你,这个变化不会影响到你。如果你只申请了权限组中的某些权限,却用了同组的其他权限,那么你就需要去适配一下了。
那么怎么适配呢,如果你去检查之前每个申请权限的地方,未免太过麻烦。那么你可以根据你项目中的Manifest
文件中需要的权限与权限组去对比,整理出你需要申请的各个权限组。比如你需要android.permission.CALL_PHONE
(打电话)与android.permission.READ_PHONE_STATE
(读取手机状态) 这两个权限。那么你就整理出了类似下面的类。
public class PermissionGroup {//Phone权限public static String[] PHONE = new String[] {Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE};...
}
到时在申请权限时就可以直接获取它,一次将它们都申请了。这样就防止了遗漏某个权限,导致的异常。
注意:8.0中PHONE
权限组新增两个权限:
ANSWER_PHONE_CALLS
:允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用 acceptRingingCall() 函数。
READ_PHONE_NUMBERS
:权限允许您的应用读取设备中存储的电话号码。
3.通知适配
其实8.0在通知这里变化还挺多的,比如通知渠道、通知标志、通知超时、背景颜色的等,详细的说明可以去看官方的Android 8.0 功能和 API。虽然变化很多,但是国内的机子貌似支持的不多。。。我在小米的文档中了解到,Android 已将通知渠道的逻辑纳入 Android Compatibility Definition Document (CDD) 中,意味着所有 Android 厂商都必须支持。所以我们可以放心的去适配。
通知渠道:Android 8.0 引入了通知渠道,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。
我个人很喜欢这个新特性。也就是说,我们可以将我们给用户的通知进行分类,我用高德地图app来举例,从左到右分别是小米(MIUI10)、华为(EMUI 8.1.0)、一加(氢OS 5.1):
可以看到高德地图分的很细致,分为四个组共13个类别(华为貌似对组不生效)。这样有个好处,我们可以控制我们想收到的通知,比如我不喜欢运营活动通知,那我就可以把它关闭。这样避免大量的不必要通知,否则使得用户觉得烦,一棒子打死。直接关闭你的允许通知。当然了,大量app都还没有适配,适配的也都分的不是很细致,比如下图的QQ。(没有对比就没有伤害)
当然更重要的问题是,如果不去适配,可能通知都不会弹出来。那么适配的方法如下:
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, "广告");//创建groupnotificationManager.createNotificationChannelGroup(group);//channelId要唯一String channelId = "channel_001";NotificationChannel adChannel = new NotificationChannel(channelId,"推广信息", NotificationManager.IMPORTANCE_DEFAULT);//补充channel的含义(可选)adChannel.setDescription("推广信息");//将渠道添加进组(先创建组才能添加)adChannel.setGroup(groupId);//创建channelnotificationManager.createNotificationChannel(adChannel);//创建通知时,标记你的渠道idNotification 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);}}
效果如下:
华为手机当只有一个渠道时,不会显示,会当做默认通知处理,除非一个以上。
注意:当Channel已经存在时,后面的
createNotificationChannel
方法仅能更新其name/description,以及对importance进行降级,其余配置均无法更新。所以如果有必要的修改只能创建新的渠道,删除旧渠道。
删除渠道代码如下:
private void deleteNotificationChannel(String channelId){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);mNotificationManager.deleteNotificationChannel(channelId);}}
4.悬浮窗适配
使用 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
}
当然记得需要有权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
再进行判断:
5.安装APK
Android 8.0去除了“允许未知来源”选项,所以如果我们的App有安装App的功能(检查更新之类的),那么会无法正常安装。
首先在AndroidManifest
文件中添加安装未知来源应用的权限:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
这样系统会自动询问用户完成授权。当然你也可以先使用 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 {//安装应用}}//接收“安装未知应用”权限的开启结果@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_CODE_UNKNOWN_APP) {installAPK();}}
对于权限组、悬浮窗还有安装未知来源应用的权限适配,我们也可以使用AndPermission。这样更加便捷。
6.透明主题的Activity
这个是在targetSdk=27,Android为8.0的手机时,出现的bug(因为官方已经在8.1修复)。问题的探究可以查看这里。
只有全屏不透明的activity才可以设置方向。否则报错如下:
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientationat android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2957)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)at android.app.ActivityThread.-wrap11(Unknown Source:0)at adroid.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)at android.os.Handler.dispatchMessage(Handler.java:105)at android.os.Looper.loop(Looper.java:164)at android.app.ActivityThread.main(ActivityThread.java:6944)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
容易中枪的比如微信分享、支付的回调页面,我们习惯设为透明。
解决办法:
- 要么去掉对应activity中的
screenOrientation
属性,或者对应设置方向的代码。 - 要么舍弃透明效果,在它的
Theme
中添加:
<item name="android:windowIsTranslucent">false</item>
方案2最好是添加 values-v26
目录,单独处理8.0版本。个人推荐方案1,。
7.集合的处理
现在,AbstractCollection.removeAll(null)
和 AbstractCollection.retainAll(null)
始终引发 NullPointerException
;之前,当集合为空时不会引发 NullPointerException
。所以我们需要做判空处理。
8.后台执行限制
后台服务限制:处于空闲状态时,应用可以使用的后台服务存在限制。 这些限制不适用于前台服务,因为前台服务更容易引起用户注意。
广播限制:除了有限的例外情况,应用无法使用清单注册隐式广播。 它们仍然可以在运行时注册这些广播,并且可以使用清单注册专门针对它们的显式广播。
在大多数情况下,应用都可以使用 JobScheduler
克服这些限制。 这种方式让应用安排为在未活跃运行时执行工作,不过仍能够使系统可以在不影响用户体验的情况下安排这些作业。关于的用法可以参考官方例子:android-JobScheduler。
—————————————————————18.10.23 补充——————————————————————————
后台任务google推荐方案使用 WorkManager
,WorkManager
可以自动维护后台任务,同时可适应不同的条件,同时满足后台Service
和静态广播,内部维护着JobScheduler
,而在6.0以下系统版本则可自动切换为AlarmManager
!有兴趣的可以了解一下。
当然还有后台位置的限制需要去注意。
9.参考
1.MIUI 10 通知类别 / Channel 适配
2.Create and Manage Notification Channels
3.Presentation of Notifications
4.Android 实现应用更新适配 Android O
Android 8.0适配指北相关推荐
- Android 9.0 适配指南
又到了我一年一度写Android适配文章的时间,本身这篇应该会早几个月发出来,但是前两三个月主要忙于Flutter的项目,所以这篇文章才姗姗来迟.不过毕竟是9.0的适配,还不算太晚哈! 1.前言 国内 ...
- Android 内存泄漏分析指北
android 内存泄漏分析指北 简单来说内存泄漏就是当对象不再被应用程序使用,但是垃圾回收器却不能移除它们,因为它们正在被引用 java 垃圾回收介绍: Java 虚拟机运行所管理的内存包括以下几个 ...
- 小老弟!听说你在搞Android 10.0 适配,看这篇就妥了!
点击上方"何俊林",马上关注,每天早上8:50准时推送 真爱,请置顶或星标 转载自公众号:刘望舒,作者: 吃猫猫的鱼 地址:https://juejin.im/post/5cad5 ...
- android 根目录uri,Android FileProvider 踩坑指北
前言 从 Android N(7.0) 开始,将严格执行 StrictMode 模式.而从 Android N 开始,将不允许在 App 间,使用 file:// 的方式,传递一个 File ,否者会 ...
- 小米7.0 android 图片裁剪失败,Android 7.0适配 -- FileProvider 拍照、选择相册、裁切图片, 小米机型适配...
需求: 最近把APP的TargetSdk从21提高至25后,测试时, 在Android7.0以上的系统上,爆出了一些异常. 在个别小米等机型也存在一些异常. 问题分析: FileUriExposedE ...
- Uni-app APP开发、适配指北
跨端三剑客 近几年来,随着移动端开发的需求的不断变大,跨端框架也在不断的蓬勃发展,随之时间的推移,有三个跨端框架凭借其各自的优势,出现在大家的眼前Uniapp.Flutter.React Native ...
- Android 7.0 适配 FileProvider相机 相册 裁剪的使用
Android7.0又加了一些新的东西,例如多窗口 通知栏发消息 等等,但是这些是一些功能的改变,可以说是很炫的进步,而我要说的是FileProvider这个杀千刀的Api,先来看一下官方解释: 上面 ...
- 谈谈Android 6.0 的动态权限管理
Android适配系列: Android 6.0 的动态权限管理 Android 7.0脱坑指南 Android 8.0适配指北 Android 9.0 适配指南 1.前言 大家都知道Android ...
- Android 7.0脱坑指南
本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发. Android适配系列: Android 6.0 的动态权限管理 Android 7.0脱坑指南 Android 8.0适配指北 ...
最新文章
- 好好学习网--2009年十大新兴企业技术:MapReduce折桂
- Google下的这盘“小”棋
- OpenWrt——配置L2TP客户端
- 猛料来啦!Autodesk全线产品二次开发视频录像下载!!
- oracle silent 安装
- python surprise库_Python推荐系统库--Surprise实战
- 现在出纳记账手写还是用计算机,请问:出纳现金日记账和银行日记账是否一定要用手写归档能否用电脑打印归档?...
- k均值聚类算法原理和TensorFlow实现
- 【天线的主要技术指标及其含义-带宽 】
- 刘辉 清华大学计算机,长郡学子又揽佳绩!CCF NOI 2017冬令营金牌数全国第一
- 数据埋点日志awk脚本快速入库
- 解决H5 在安卓手机上软键盘遮盖input的问题
- 极坐标变换原理和用法
- python pandas读取excel时动态确定标题行所在行数
- Learning Spatio-Temporal Transformer for Visual Tracking——精读笔记
- 5 JMeter断言-Jmeter响应中出现乱码时
- 游戏中的人工智能AI设计
- 89C51单片机蜂鸣器演奏两只老虎-源码
- PPPoE拨号过程解析
- 流式布局FlowLayout使用
热门文章
- 怎么启动u盘的linux系统文件夹里,怎么制作Linux系统安装盘,U盘启动盘
- AutoCAD的命令脚本scr入门
- bose qc35一代耳机换海绵垫记录
- matlab 电流源逆变器,电流逆变器的MATLAB仿真.doc
- android 广播解绑,Android四大组件之BroadCast
- C++入门第一课—“从入门到爱上”
- 解决:mode, rawmode = _fromarray_typemap[typekey]KeyError: ((1, 1, 9), ‘|u1‘)问题
- JavaScript里面的return的用法
- URI、URL、URN三者之间的关系
- linux mrtg 安装,mrtg-linux安装步骤