Android 9适配经验总结
目录
- 四大组件适配
- Activity启动方式适配
- Service启动方式适配
- 前台服务需要添加权限
- 限制静态广播的接收
- 限制ContentResolver数据更新操作
- 权限与安全相关主要适配点
- 运行时动态权限申请
- 默认不支持 http 请求
- SharedPreferences 适配
四大组件适配
Android 应用的开发离不开 Android 四大组件的使用,Android 四大组件分别是:Activity,用于展示前台页面;Service,用于执行后台任务;BroadCast,用于组件之间的通信;ContentProvider,用于应用间数据的分享。
Activity启动方式适配
Activity 组件用于和用户进行交互,在 Android 9 版本中,系统对于应用进程的任务栈做了调整,Activity 组件是运行在任务栈(task stack)中,而其它三大组件 Broadcast、Service、ContentProvider不需要运行在任务栈中,由这些组件打开 Activity 时需要为这个 Activity 的运行指定一个新的任务栈,否则会导致应用崩溃,并抛出异常信息:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
适配方法,在 Broadcast、Service、ContentProvider 这些组件中拉起 Activity,需要添加FLAG_ACTIVITY_NEW_TASK
标志,这样便会在拉起 Activity 的同时,创建出一个新的任务栈:
Intent intent = new Intent(context, "xxxxx");//xxxxx 表示 Activity 的类名
intent.addFlag(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActiviy(intent);
Service启动方式适配
Service 是 Activity 外最常用的组件之一,它承接着用户处理后台任务的需求,是一种长生命周期的,没有可视化界面,运行于后台的一种服务程序。在 Android 5.0 之后 google 出于安全的角度禁止了隐式声明 Intent 来启动 Service,隐式声明即是不通过指定 Service 包名和类名,而是通过Intent 过滤机制,设置 intent 的 action、dataType 等方式根据 intent 匹配规则来查找并调用 Service:
Intent intent = new Intent();
intent.setAction("xxxx");//xxxx 为Service 在 IntentFilter 中配置的 action
context.startService(intent);
隐式启动 Service 会产生一个问题,就是传入的 action、dataType 可能会匹配到别的应用的后台服务上去,Service 是在后台运行的,不被用户所感知,因此通过隐式方式启动 Service 可能拉起的不是实际想要启动的服务,从而会产生错误。
为了防止隐式启动 Service 带来的问题,从 Android5.0 开始系统就禁止隐式启动 Service 的方式,上述代码在 Android9 系统上运行时会导致应用崩溃,并抛出异常:
Service Intent must be explicit…
适配方法
解决方式一: 将隐式启动转换为显式启动,通过指定包名、类名的方式拉起服务:
ComponentName cn = new ComponentName("包名","类名");
Intent intent = new Intent(cn);//显示启动 Service
context.startService(intent);
解决方式二:调用 intent 的 setPackage 方法指定包名:
Intent intent = new Intent();
intent.setAction("xxxx");//xxxx 为Service 在 IntentFilter 中配置的 action
intent.setPackage("应用包名");
context.startService(intent);
前台服务需要添加权限
在 Android 9.0 中,为了防止前台服务被滥用,比如各种通知信息,系统规定应用在使用前台服务之前必须先申请 FOREGROUND_SERVICE
权限,否则就会抛出 SecurityException
异常。
java.lang.SecurityException: Permission Denial: startForeground from pid=xxxx, uid=xxxx requires android.permission.FOREGROUND_SERVICE
适配方法,在 AndroidManifest 中添加权限声明:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
通过这样方式便可以申请前台服务的运行权限,前台服务的创建才会正常执行。
限制静态广播的接收
应用监听广播可以通过动态注册和静态注册两种方式,动态注册就是在程序运行起来后,调用注册方法进行注册,静态注册则是把广播注册放在 Manifest 配置文件中。
在一些场景中,比如监听开机启动广播来拉起应用,是需要通过静态注册方式来实现。但对于需要应用正常启动后才能对广播进行正确处理的场景,则应用使用动态注册的方式,这时如果采用静态注册的方式,在应用没有启动时,收到广播可能不会得到正确的处理,同时都采用静态注册的方式也会影响广播传递的效率,因为很多未启动的应用也会被广播唤醒。
也是出于这样的考虑,Android 9.0 之后,隐式广播将会被全面限制,用户的自定义广播和大部分系统广播通过隐式注册的方式,即在 AndroidManifest 中注册的 Receiver 的方式将不能够生效。
适配方法,使用动态注册的方式注册广播监听,如下代码:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("xxxx");//xxxx 标识广播 action
context.registerReceiver(broadcastReceiver, intentFilter);
采用代码中动态注册广播的方式不仅是 Android9 系统的要求,也是一种比较好的开发习惯,这有利于对广播的使用进行控制,在应用功能已经初始化完成的时候添加广播的监听,确保广播到来时的功能执行能正常进行。
限制ContentResolver数据更新操作
Android 提供 ContentProvider/ContentResolver 组件来让用户开放自己应用中的数据或者访问别的应用的数据,为了防止用户数据监听被滥用,从 Android8.0 系统起,通过 ContentResolver的registerContentObserver 方法监听应用数据变化的操作被加以限制,直接操作会报出如下错误,并导致应用崩溃:
Failed to find provider xxx for user xxx; expected to find a valid ContentProvider for this authority
适配方法,在 AndroidManifest.xml 文件中定义一个 Provider,使用监听数据更新的目标 ContentProvider的 authorities 作为这个 provider 的 authorities:
<providerandroid:name="com.xx.content.ContentProvider"android:authorities="com.xxx.androidclient" android:enabled="true" android:exported="false">
</provider>
权限与安全相关主要适配点
运行时动态权限申请
Google 在 Android 6.0 开始引入了权限申请机制,将所有权限分成了正常权限和危险权限。应用的相关功能每次在使用危险权限时需要动态的申请并得到用户的授权才能使用。
系统权限分为两类:正常权限和危险权限。
正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
危险权限会授予应用访问用户机密数据的权限。如果应用在其清单中列出了危险权限,则用户必须明确批准应用使用这些权限。
危险权限主要是和获取用户数据有关,应用会用到的危险权限包括:
android.permission.READ_EXTERNAL_STORAGE 读取外部存储数据
android.permission.WRITE_EXTERNAL_STORAGE 向外部存储写入数据
常用的正常权限包括:
BLUETOOTH 使用蓝牙权限
BROADCAST_STICKY 粘性广播
CHANGE_NETWORK_STATE 改变网络状态
CHANGE_WIFI_STATE 控制 WiFi 开关,改变 WiFi 状态
GET_PACKAGE_SIZE 获取应用安装包大小
INTERNET 网络权限
RECEIVE_BOOT_COMPLETED 监听启动广播
REQUEST_INSTALL_PACKAGES 安装应用程序
WRITE_SYNC_SETTINGS 修改系统设置
适配方法,应用运行过程中,动态申请需要的危险权限,如下代码:
requestPermissions(final @NonNull Activity activity,final @NonNullString[] permissions, final int requestCode)//permissions 即为需要申请权限列表
默认不支持 http 请求
出于网络安全的考虑,Android9 系统上,应用将被禁止使用 http 协议进行网络数据传输,而必须使用 https 安全网络协议,如果应用中使用了 http 协议传输数据,将会抛出下面的错误:
java.net.UnknownServiceException: CLEARTEXT communication to xxxx not permitted by network security policy
最好的适配方式是修改所有的网络接口,改为 https 协议;除了服务端接口改动,Google 也提供了两个方案来让客户端支持 http 协议。
方案 1:
在 AndroidManifest.xml 中的 application 节点添加以下配置, 允许所有明文请求。
<application
... android:usersCleartextTraffic=“true”
>
方案 2:
添加自定义的网络安全配置在 res 目录下新建 xml 文件夹,添加 network_security_config.xml
文件:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
AndroidManifest.xml 中的 application 添加:
<manifest ... ><applicationandroid:networkSecurityConfig="@xml/network_security_config">... </application>
</manifest>
SharedPreferences 适配
SharedPreferences 是 Android 中的数据存储组件,原先针对数据访问没有严格的限制,
Android9.0 以后给 SharedPreferences 引入数据文件访问权限控制,去除了被外部应用读取数据的权限,设置 MODE_WORLD_READABLE
或 MODE_WORLD_WRITEABLE
会触发安全异常。
适配方法
方案 1:
将MODE_WORLD_READABLE
模 式 换 成MODE_PRIVATE
, 通 过 这 种 方 式 将 应 用 的SharedPreference 数据访问权限设置为私有,以防止别的应用对 SharedPreference 数据进行任意的读写操作,保证了应用自身的安全性,对于可以向别的应用暴露的数据,也可以通过ContentProvider组件实现数据共享。
方案 2:
对于需要全局读写的功能,可使用其他方式替换 SharedPreferences,例如DataStore
、MMKV
等。
相关文章:
Android 9适配经验总结
目录四大组件适配Activity启动方式适配Service启动方式适配前台服务需要添加权限限制静态广播的接收限制ContentResolver数据更新操作权限与安全相关主要适配点运行时动态权限申请默认不支持 http 请求SharedPreferences 适配四大组件适配 Android 应用的开发离不开 Android 四…...
定时任务调度方案——Xxl-Job
定时任务调度方案 随着系统规模的发展,项目的组织结构以及架构越来越复杂,业务覆盖的范围越来越广,定时任务数量日益增多,任务也变得越来越复杂,尤其是为了满足在用户体量日历增大时,系统能够稳定运行&…...
操作系统引导
操作系统是一种程序,程序以数据的形式存放在硬盘中,而硬盘通常分为多个区,一个计算机中又有多个或多种外部设备。 操作系统引导指的是计算机利用CPU运行特定程序,通过程序识别硬盘,识别硬盘分区,识别硬盘分…...
[C#] 多线程单例子,分为阻塞型和分阻塞型, 在unity里的应用
在单例中使用多线程时,需要注意以下几点: 线程安全:在多线程环境下,单例对象可能被多个线程同时访问,因此需要确保单例的线程安全,避免出现数据竞争等问题。 对象创建:如果在单例对象的构造函数…...
使用MAT进行内存分析,并找到OOM问题
前言 在处理一次现场问题时,发现服务还在运行,但是出现假死情况,后通过分析GC日志以及使用MAT分析确定问题是内存溢出OutOfMemery(OOM);这里只记录MAT分析学习过程,最近工作忙,补记录。 GC日志分析 首先,如…...
初识Python
目录初识Python1.Python简介Python的优缺点Python的应用领域2.安装Python解释器Windows环境Linux环境macOS环境3.运行Python程序确认Python的版本编写Python源代码运行程序代码中的注释4.Python开发工具IDLE - 自带的集成开发工具IPython - 更好的交互式编程工具Sublime Text -…...
tmux终端复用软件
一、安装[rootpool-100-1-1-159 test]# yum install tmux [rootpool-100-1-1-159 test]# yum search tmux Repository extras is listed more than once in the configuration Last metadata expiration check: 0:33:52 ago on Fri 03 Mar 2023 09:10:34 AM CST.Name Exactly M…...
IO详解(文件,流对象,一些练习)
目录 文件 文件概念 文件的路径 路径有俩种表示风格 文件类型 如何区分文本文件还是二进制文件? java对文件的操作 File类中的一些方法 流对象 流对象的简单概念 java标准库的流对象 1.字节流,(操作二进制数据的) 2.字符流 (操作文本数据的) 流对象最核心的四个…...
SpringCloud全家桶— — 【1】eureka、ribbon、nacos、feign、gateway
SpringCloud全家桶— — 组件搭建 1 Eureka 1.1 Eureka-server 创建eureka-server的SpringBoot项目 ①导入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId…...
【线程安全篇】
线程安全之原子性问题 x ,在字节码文件中对应多个指令,多个线程在运行多个指令时,就存在原子性、可见性问题 赋值 多线程场景下,一个指令如果包含多个字节码指令,那么就不再是原子操作。因为赋值的同时,…...
错误:EfficientDet网络出现“No boxes to NMS“并且mAP:0.0的解决方案
近日,在使用谷歌新推出来的一个网络EfficientDet进行目标检测训练自己的数据集的时候,出现了如下错误: 其中项目开源地址是:https://github.com/toandaominh1997/EfficientDet.Pytorch 上面截图中的1和2代表我的类别名称。读者可…...
python的opencv操作记录13——区域生长及分水岭算法
文章目录图像区域基本算法——形态学运算腐蚀与膨胀开运算与闭运算opencv中的形态学运算距离计算——distanceTransform函数连通域连通的定义计算连通域——connectedComponents连通域实验基于区域的分割区域生长算法自定义一个最简单区域生长算法实现区域分割一般区域分割open…...
一文看懂网上下单的手机流量卡为什么归属都是随机的!
最近很多网上下单的小伙伴们心中似乎都有一个疑问。那就是网上很多手机卡、流量卡都不能自选号码和归属地,就算能自选号码,归属地也是随机的而且很多都不会跟你说具体的城市,这是为什么呢?莫非其中有什么不可告人的秘密吗?小伙伴…...
python Pytest生成alluer测试报告的完整教程
1.下载allure包到本地,解压 网上很多资料,这边不提供了 2.配置环境变量 将上面解压后bin文件的路径复制,添加到环境变量Path下 3.验证环境变量配置是否功 在cmd中输入allure,回车 。查看allure是否成功: 4.pyc…...
4-spring篇
ApplicationContext refresh的流程 12个步骤 prepareRefresh 这一步创建和准备了Environment对象,并赋值给了ApplicationContext的成员变量 要理解Environment对象的作用 obtainFreshBeanFactory ApplicationContext 里面有一个成员变量,Beanfactory b…...
提升 Web 应用程序的性能:如何使用 JavaScript 编写缓存服务
缓存是一种重要的优化技术,用于加速数据访问和降低服务器负载。缓存存储经常访问的数据,以便在需要时可以快速检索。在本文中,我们将探索如何使用简单的数据结构在 JavaScript 中编写缓存服务。 编码缓存服务的第一步是定义将用于访问缓存的…...
供应商绩效管理指南:挑战、考核指标与管理工具
管理和优化供应商绩效既关键又具有挑战性。要知道价格并不是一切,如果你的供应商在商定的价格范围内向你开具发票,但服务达不到标准或货物不合格,你也无法达到节约成本的目标。 供应商绩效管理可以深入了解供应商可能带来的风险,…...
干货文稿|详解深度半监督学习
分享嘉宾 | 范越文稿整理 | William嘉宾介绍Introduction to Semi-Supervised Learning传统机器学习中的主流学习方法分为监督学习,无监督学习和半监督学习。这里存在一个是问题是为什么需要做半监督学习?首先是希望减少标注成本,因为目前可以…...
信箱|邮箱系统
技术:Java、JSP等摘要:在经济全球化和信息技术飞速发展的今天,通过邮件收发进行信息传递已经成为主流。目前,基于B/S(Browser/Server)模式的MIS(Management information system)日益…...
JS数组拓展
1、Array.from Array.from 方法用于将两类对象转为真正的数组: 类似数组的对象,所谓类似数组的对象,本质特征只有一点,即必须有length属性。 因此,任何有length属性的对象,都可以通过Array.from方法转为数组 和 可遍历…...
一道很考验数据结构与算法的功底的笔试题:用JAVA设计一个缓存结构
我在上周的笔试中遇到了这样一道题目,觉得有难度而且很考验数据结构与算法的功底,因此Mark一下。 需求说明 设计并实现一个缓存数据结构: 该数据结构具有以下功能: get(key) 如果指定的key存在于缓存中,则返回与该键关联的值&am…...
(10)C#传智:命名空间、String/StringBuilder、指针、继承New(第10天)
内容开始多了,慢品慢尝才有滋味。 一、命名空间namespace 用于解决类重名问题,可以看作类的文件夹. 若代码与被使用的类,与当前的namespace相同,则不需要using. 若namespace不同时,调用的方法:…...
基于Jetson Tx2 Nx的Qt、树莓派等ARM64架构的Ptorch及torchvision的安装
前提 已经安装好了python、pip及最基本的依赖库 若未安装好点击python及pip安装请参考这篇博文 https://blog.csdn.net/m0_51683386/article/details/129320492?spm1001.2014.3001.5502 特别提醒 一定要先根据自己板子情况,找好python、torch、torchvision的安…...
MySQL存储引擎详解及对比和选择
什么是存储引擎? MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善…...
【推拉框-手风琴】vue3实现手风琴效果的组件
简言 在工作时有时会用到竖形手风琴效果的组件。 在此记录下实现代码和实现思路。 手风琴实现 结构搭建 搭建结构主要实现盒子间的排列效果。 用flex布局或者其他布局方式将内容在一行排列把每一项的内容和项头用盒子包裹, 内容就是这一项要展示的内容…...
滑动窗口最大值:单调队列
239. 滑动窗口最大值 难度困难2154收藏分享切换为英文接收动态反馈 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例…...
负载均衡算法
静态负载均衡 轮询 将请求按顺序轮流地分配到每个节点上,不关心每个节点实际的连接数和当前的系统负载。 优点:简单高效,易于水平扩展,每个节点满足字面意义上的均衡; 缺点:没有考虑机器的性能问题&…...
C语言数组二维数组
C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的变量,比如 runoob0、runoob1、…、runoob99,而是…...
7年测试工程师,裸辞掉17K的工作,想跳槽找更好的,还是太高估自己了....
14年大学毕业后,在老师和朋友的推荐下,进了软件测试行业,这一干就是7年时间,当时大学本来就是计算机专业,虽然专业学的一塌糊涂,但是当年的软件测试属于新兴行业,人才缺口比较大,而且…...
企业为什么需要做APP安全评估?
近几年新型信息基础设施建设和移动互联网技术的不断发展,移动APP数量也呈现爆发式增长,进而APP自身的“脆弱性”也日益彰显,这对移动用户的个人信息及财产安全带来巨大威胁和挑战。在此背景下,国家出台了多部法律法规,…...
wordpress idstore/搜索引擎优化关键词的处理
1)将扫描枪电缆的一端与网络电缆的水晶头连接到扫描枪底部的插座。 当您听到“喀哒”声时,请再次将其拉回。 电缆不会滑出以表示已插入。另一端插入计算机上的相对端口号(此处以USB接口为例)。 此时,计算机的右下角将提醒您自动安装硬件配置驱动程序软件…...
wordpress 数据库连接文件/郑州高端网站制作
flutter 运行别人项目 包无法导入报错:Target of URI doesnt exist package:flutter/material.dart 解决方法参考文章: (1)flutter 运行别人项目 包无法导入报错:Target of URI doesnt exist package:flutter/materia…...
wordpress 游客留言/怎么自己开网站
前提条件: (1) zabbix服务器端已经成功安装并且运行。 (2) zabbix客户端已经成功建立并且运行。 1 下载并且安装msmtp软件 Wget http://sourceforge.net/projects/msmtp/files/msmtp/1.4.32/msmtp-1.4.32.tar.bz2/download tar jxvf msmtp-1.4.32.tar.bz2 cd ms…...
汨罗住房和城乡建设局网站/百度指数爬虫
其实只要掌握了import的用法,export也就分分钟的事就搞定了。因为很多也是通用的,比如–connect,–username,–password,–table,–columns,-m等,还比如上节所说的direct,options-file,job。最大的不同可能就是–target-dir换成了–export-di…...
网址导航网站建设/廊坊自动seo
PIM 文件疑难解答常见的 PIM 打开问题Avid Pro Tools 不在你尝试加载 PIM 文件并收到错误,例如 “%%os%% 无法打开 PIM 文件扩展名”。 发生这种情况时,通常是由于 %%os%% 中缺少 Avid Pro Tools。 通过双击打开 PIM 的典型路径将不起作用,因…...
网站建设2017排名/微信app小程序开发
昨天做了什么: 注册页面以及通过网上注册已基本完成 今天要做什么: 今天和明天都打算完成最后的连接数据库工作 遇到什么困难 目前没有转载于:https://www.cnblogs.com/bai123/p/7007429.html...