软件测试(五)自动化 selenium
文章目录
- 自动化测试
- 单元测试:
- 单元测试:
- UI自动化
- selenium工具
- 定义
- 特点:
- 原理:
- selenium+java环境搭建
- Selenium+API
- 获取测试结果:
- 添加等待
- 浏览器操作
- 键盘事件
- 鼠标事件
- 多层框架/窗口定位
- 下拉框处理
- 弹窗处理
- 上传文件操作
- 关闭浏览器
- 窗口的切换
- 截图
自动化测试
自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结果。将人为驱动的测试行为转化为机器(代码)执行的过程。(简单而言其实就是降低重复性的工作(大部分是Python))
自动化测试的具体实现,应该是包含下面七个过程的。
- 分析:总体把握系统逻辑,分析出系统的核心体系架构。
- 设计:设计测试用例,测试用例要足够明确和清晰,覆盖面广而精
- 实现:实现脚本,有两个要求一是断言,二是合理的运用参数化。
- 执行:执行脚本远远没有我们想象中那么简单。脚本执行过程中的异常需要我们仔细的去分析原因。
- 总结:测试结果的分析,和测试过程的总结是自动化测试的关键。
- 维护:自动化测试脚本的维护是一个难以解决但又必须要解决的问题。
- 分析:在自动化测试过程中深刻的分析自动化用例的覆盖风险和脚本维护的成本。
自动化的分类:单元测试,接口测试,UI自动化测试
单元测试:
最大的投入应该在单元测试上,单元测试运行的频率也更加高。
单元测试:
接口测试就是API测试,相对于UI自动化API自动化更加容易实现,执行起来也更稳定。
接口自动化的有以下特点:
- 可在产品前期,接口完成后介入
- 用例维护量小
- 适合接口变动较小,界面变动频繁的项目
常见的接口自动化测试工具有,RobotFramework,JMeter,SoapUI,TestNG+HttpClient,Postman等。
UI自动化
虽然测试金字塔告诉我们尽量多做API层的自动化测试,但是UI层的自动化测试更加贴近用户的需求和软件系统的实际业务。并且有时候我们不得不进行UI层的测试。
UI自动化的特点:
- 用例维护量大
- 页面相关性强,必须后期项目页面开发完成后介入
- UI测试适合与界面变动较小的项目
UI层的测试框架比较多,比如Windows客户端测试的AutoIT,web测试的selenium以及TestPlanteggPlant,Robot framework,QTP等
selenium工具
定义
selenium是用来做web自动化测试框架。
特点:
- 兼容各种浏览器,支持各种平台,支持各种语言
- 小巧,对于不同的语言它只是一个包而已,而QTP 需要下载安装1个多G 的程序
- 有丰富的API
- 支持分布式测试用例的执行,可以把测试用例分布到不同的测试机器执行,相当于分 发机的功能。
原理:
自动化脚本:通过编写代码
webDriver:需要我们去下载
浏览器:edge浏览器,Chrome浏览器
selenium+java环境搭建
- 浏览器这里用Chrome浏览器:官网下载(注意自己浏览器的版本:在浏览器关于里查看)
- 下载与之对应的浏览器驱动:webDirver:官网下载
- 将压缩包解压后驱动放入java安装的jdk中;
- 打开idea创建项目,导入依赖pom
<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>3.141.59</version></dependency>
- 创建测试化代码
public class Main {public static void main(String[] args) {ChromeOptions options=new ChromeOptions();//允许所有的请求options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);//打开一个网页webDriver.get("https://www.baidu.com");}
}
- 运行代码
- 此时环境就配好了
Selenium+API
webdriver 提供了一系列的对象定位方法,常用的有以下几种:
id,name,class name,link textpartial,link text,tag name,xpath,css selector
这里的重点是css,xpath
public class Main {public static void main(String[] args) {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");//找到百度搜索框// WebElement element= webDriver.findElement(By.cssSelector(".s_ipt"));WebElement element=webDriver.findElement(By.xpath("//*[@id=\"kw\"]"));//输入一个软件测试element.sendKeys("软件测试");}
}
定位元素findElement:css
css选择语法:
id选择器:#id
类选择:.class
标签选择器:标签名
后代选择器:父级选择器,子级选择器
xPath :
绝对路径:从根目录出发寻找
相对路径:(常用)
1. 相对路径+索引://from/span[2]/input
2. 相对路径+属性://input[@class="s_ipt"]
3. 相对路径+通配符://*[@*="s_ipt"]
4. 相对路径+文本匹配://a[text()="新闻"]
获取测试结果:
public class Main {public static void main(String[] args) throws InterruptedException {int flag=0;ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");//找到百度搜索框// WebElement element= webDriver.findElement(By.cssSelector(".s_ipt"));WebElement element=webDriver.findElement(By.xpath("//*[@id=\"kw\"]"));//输入一个软件测试element.sendKeys("软件测试");//点击一下百度按钮webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);//校验//找到搜索结果List<WebElement> elements= webDriver.findElements(By.cssSelector("a em"));for (int i=0;i<elements.size();i++){if (!elements.get(i).getText().equals("测试")){flag=1;System.out.println("测试通过");break;}}if (flag==0){System.out.println("测试不通过");}}
}
webdriver 中比较常用的操作对象的方法有下面几个:
- click: 点击对象
- sendKeys: 在对象上模拟按键输入
- clear: 清除对象输入的文本内容
- submit: 提交(必须from标签中,不然会报错)
- text: 用于获取元素的文本信息
- getAttribute 获取控件中的属性值
private static void test02() {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");String button_value=webDriver.findElement(By.cssSelector("#su")).getAttribute("value");if (button_value.equals("百度一下")){System.out.println("测试通过");}else {System.out.println(button_value);System.out.println("测试不通过");}}
添加等待
sleep休眠:添加休眠非常简单,我们需要引入time 包,就可以在脚本中自由的添加休眠时间了,这里的休眠指固定休眠
隐式等待:通过添加implicitlyWait() 方法就可以方便的实现智能等待;implicitlyWait(30)的用法比time.sleep()更智能,后者只能选择一个固定的时间的等待,前者可以在一个时间范围内智能的等待
private static void test02() {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");String button_value=webDriver.findElement(By.cssSelector("#su")).getAttribute("value");//隐式等待webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.DAYS);if (button_value.equals("百度一下")){System.out.println("测试通过");}else {System.out.println(button_value);System.out.println("测试不通过");}}
隐式地等待并非一个固定的等待时间,当脚本执行到某个元素定位时,如果元素可以定位,则继续执行;如果元素定位不到,则它以轮询的方式不断的判断元素是否被定位到。直到超出设置的时长。
显示等待:
private static void test02() {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");String button_value=webDriver.findElement(By.cssSelector("#su")).getAttribute("value");
// webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.DAYS);WebDriverWait wait= new WebDriverWait(webDriver,3000);wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#help > a:nth-child(1)")));}
隐式等待和显示等待区别:
显示等待:可以针对某一个地方进行等待
隐式等待:对全局进行等待
浏览器操作
- 浏览器前进,浏览器退后
private static void test03() {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");webDriver.findElement(By.cssSelector("#su")).click();//浏览器后退webDriver.navigate().back();//浏览器前进webDriver.navigate().forward();//浏览器刷新webDriver.navigate().refresh();}
- 浏览器滚动条
#将浏览器滚动条滑到最顶端
document.documentElement.scrollTop=0
#将浏览器滚动条滑到最底端
document.documentElement.scrollTop=10000
#将浏览器滚动条滑到最底端, 示例
((JavascriptExecutor)webDriver).executeScript(“document.documentElement.scrollTop=10000”);
其中,executeScript(script, *args),在当前窗口/框架同步执行javaScript
private static void test03() {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");webDriver.findElement(By.cssSelector("#su")).click();//浏览器后退webDriver.navigate().back();//浏览器前进webDriver.navigate().forward();//浏览器刷新webDriver.navigate().refresh();//滑动((JavascriptExecutor)webDriver).executeScript("document.documentElement.scrollTop=10000");}
- 设置浏览器宽、高
webDriver.manage().window().setSize(new Dimension(1000,1000));
- 浏览器最大化
webDriver.manage().window().maximize();
- 浏览器全屏
webDriver.manage().window().fullscreen();
键盘事件
要使用键盘按键,必须引入keys 包:
通过send_keys()调用按键:
sendkeys(Keys.TAB) # TAB
sendkeys(Keys.ENTER) # 回车
sendkeys(Keys.SPACE) #空格键
sendkeys(Keys.ESCAPE) #回退键(Esc)
键盘组合键用法
sendkeys(Keys.CONTROL,‘a’) #全选(Ctrl+A)
sendkeys(Keys.CONTROL,‘c’) #复制(Ctrl+C)
sendkeys(Keys.CONTROL,‘x’) #剪贴(Ctrl+X)
sendkeys(Keys.CONTROL,‘v’) #粘贴(Ctrl+V)
webDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"A");
鼠标事件
Actions(driver)
生成用户的行为。所有的行动都存储在actionchains 对象。通过perform()存储的行为。
move_to_element(element)
移动鼠标到一个元素中,menu 上面已经定义了他所指向的哪一个元素
perform()
执行所有存储的行为:
Actions类
- contextClick() 右击
- doubleClick() 双击
- dragAndDrop() 拖动
- moveToElement() 移动
private static void test05() throws InterruptedException {ChromeOptions options=new ChromeOptions();options.addArguments("--remote--allow-origin=*");WebDriver webDriver=new ChromeDriver(options);webDriver.get("https://www.baidu.com");WebElement webElement=webDriver.findElement(By.cssSelector("#kw"));Actions actions=new Actions(webDriver);sleep(3000);actions.moveToElement(webElement).contextClick().perform();}
多层框架/窗口定位
获取框架
对于一个web 应用,经常会出现框架(frame) 或窗口(window)的应用,这也就给我们的定位带来了一定的困难。
通过frame的id或者name或者frame自带的其它属性来定位框架,这里switchTo.frame()把当前定位的主体切换了frame里。
窗口定位:
有可能嵌套的不是框架,而是窗口,还有真对窗口的方法:switchTo.window用法与switchTo.frame 相同。
下拉框处理
下拉框是我们最常见的一种页面元素,对于一般的元素,我们只需要一次就定位,但下拉框里的内容需要进行两次定位,先定位到下拉框对下拉框进行操作后,再定位到下拉框内里的选项。
private static void test06() {WebDriver webDriver=new ChromeDriver();webDriver.get("https://www.baidu.com");WebElement webElement=webDriver.findElement(By.cssSelector("#ShippingMethod"));Select select=new Select(webElement);//通过下标选择select.selectByIndex(3);select.selectByValue("12.5");}
弹窗处理
alert、confirm、prompt 的处理
- text 返回alert/confirm/prompt 中的文字信息
- accept 点击确认按钮
- dismiss 点击取消按钮,如果有的话
- send_keys 输入值,如果alert 没有对话框就不能用了,不然会报错
private static void test07() throws InterruptedException {WebDriver webDriver=new ChromeDriver();webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("button")).click();sleep(3000);//alert弹窗取消webDriver.switchTo().alert().dismiss();//点击按钮webDriver.findElement(By.cssSelector("button")).click();//在alert弹窗内输入值webDriver.switchTo().alert().sendKeys("123456");//alert弹窗确认webDriver.switchTo().alert().accept();}
上传文件操作
上传过程一般要打开一个本地窗口,从窗口选择本地文件添加。所以,一般会卡在如何操作本地窗口添加上传文件。
在selenium webdriver 没我们想的那么复杂;只要定位上传按钮,通过sendKeys 添加本地文件路径就可以了。绝对路径和相对路径都可以,关键是上传的文件存在。
private static void test08() {WebDriver webDriver=new ChromeDriver();webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("input")).sendKeys("本地文件目录");}
关闭浏览器
浏览器的quit和close之间的区别:
- quit关闭了整个浏览器,close关闭当前的页面。
- quit清空了浏览器的缓存,close不会清空缓存。
private static void test09() {WebDriver webDriver=new ChromeDriver();webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("#help > a:nth-child(1)"));//关闭webDriver.quit();webDriver.close();}
窗口的切换
getWindowHandle 方法获取当前
private static void test11() throws InterruptedException {WebDriver webDriver=new ChromeDriver();webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("#help > a:nth-child(1)")).click();sleep(3000);//通过这个方法getWindowHandle,获取当前的窗口句柄//通过这个方法getWindowHandles,获取所有的窗口句柄Set<String> handles= webDriver.getWindowHandles();String target_handle="";for ( String handle:handles){target_handle=handle;}webDriver.switchTo().window(target_handle);sleep(3000);webDriver.findElement(By.cssSelector("#ww")).sendKeys("新闻联播");webDriver.findElement(By.cssSelector("#s_btn_wr")).click();}
截图
导入依赖:进入Maven仓库
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version>
</dependency>
private static void test12() throws InterruptedException, IOException {WebDriver webDriver=new ChromeDriver();webDriver.get("https://www.baidu.com");webDriver.findElement(By.cssSelector("#kw")).sendKeys("测试");webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);File file=((TakesScreenshot)(webDriver)).getScreenshotAs(OutputType.FILE);FileUtils.copyFile(file,new File("D://20231022.png"));}
需要向上转型,TakesScreenshot,然后调用getScreenshotAs方法
相关文章:

软件测试(五)自动化 selenium
文章目录 自动化测试单元测试:单元测试:UI自动化 selenium工具定义特点:原理:seleniumjava环境搭建SeleniumAPI获取测试结果:添加等待浏览器操作键盘事件鼠标事件多层框架/窗口定位下拉框处理弹窗处理上传文件操作关闭…...
Android grantUriPermission的使用场景和方式
#grantUriPermission 作用 临时授权。 背景:FileProvider引入后应用之间想访问文件,都需要使用此接口。特别是两个独立的应用之间互通数据的时候。例如我们应用从图库获取文件的uri,显示在应用内的ImageView中。 #grantUriPermission 使用方…...

2023高频前端面试题-vue
1. 什么是 M V VM Model-View-ViewModel 模式 Model 层: 数据模型层 通过 Ajax、fetch 等 API 完成客户端和服务端业务模型的同步。 View 层: 视图层 作为视图模板存在,其实 View 就是⼀个动态模板。 ViewModel 层: 视图模型层 负责暴露数据给 View 层&…...

03初始Docker
一、初始Docker 1.什么是Docker 问题 ①大型项目组件复杂,运行环境复杂,部署时依赖复杂,出现兼容性问题。 ②开发,测试,生产环境有差异。不同的环境操作系统不同 解决 ①Docket将应用、依赖、函数库、配置一起打…...
1.1、Python基础-注释、变量声明及命名规则、数据类型
1.1、Python基础 Python基础1、注释2、变量3、数据类型 Python基础 1、注释 注释是给程序员看的,为了让程序员方便阅读代码,解释器会忽略注释。使用自己熟悉的语言,适当的对代 码进行注释说明是一种良好的编码习惯。 注释写法 #我是单行注…...
Python第三方库安装——使用vscode、pycharm安装Python第三方库
[TOC](Python第三方库安装——使用vscode、pycharm安装Python第三方库) # 前言 在这里介绍vscode、Pycharm安装python第三方库的方法。 操作系统:windows10 专业版 环境如下: Pycharm Comunity 2022.3 Visual Studio Code 2019 Python 3.8 pipÿ…...
【vue】组件通选方式
父子传值 props $emit 这是最基本的父子组件通讯方式。通过 props 属性将数据从父组件传递给子组件,而子组件通过触发事件($emit)将数据发送回父组件。 $children $parent 通过 $parent 属性可以访问父组件的实例,通过 $child…...
java 使用策略模式减少if
使用多态:通过使用面向对象的多态特性,可以将不同的逻辑封装到不同的类中,避免大量的 if 语句。使用继承和接口来定义通用的方法,并让具体的实现类实现这些方法。 使用设计模式:使用设计模式可以更好地组织和管理代码逻…...
第1章 引论
前言 这一章,阐述本书的目的,并简要复习离散数学以及程序设计的一些概念: 看到程序在较大输入情况下的运行性能与在适量输入情况下的运行性能具有同等重要性总结本书其余部分所需要的数学基础简要复习递归 1.1 本书讨论的内容 在许多问题当中…...
深入探究Linux文件:.sh、.swp文件的作用与意义 (linux .sh.swp)
近年来,Linux操作系统已经成为了许多服务器、云计算平台、嵌入式设备等领域的首选。Linux操作系统囊括了大量的命令和文件,而其中 .sh 和 .swp 文件是许多 Linux 用户较为熟悉的两种文件类型。那么,这两种文件的作用和意义是什么呢࿱…...

优雅的使用String字符串处理各种类型转换
文章目录 🌟 优雅的使用String字符串处理各种类型转换🍊 基本类型转字符串🍊 字符串转基本类型🍊 字符串与字符数组的转换🍊 字符串与字节数组的转换🍊 其他类型转字符串🍊 总结 📕我…...

Harmony 个人中心(页面交互、跳转、导航、容器组件)
个人中心 前言正文一、创建工程二、登录① 更换启动页面② 拓展修饰符③ 页面跳转④ 等待进度条 三、导航栏四、首页① 轮播图② 网格列表 五、我的① 带参数跳转 六、源码 前言 今天是1024,祝各位程序员们,钱多事少离家近,不秃也强bug黄。在…...

AlDente Pro for Mac: 掌控电池充电的终极解决方案
你是否曾经为了保护你的MacBook的电池,而苦恼于无法控制它的充电速度?AlDente Pro for Mac 是一款专为Mac用户设计的电池管理工具,它能帮助你解决这个问题。 AlDente Pro for Mac 是一款电池最大充电限制软件,它能够让你自由地设…...

tomcat的负载均衡、动静分离(nginx联动)
动静分离: 访问静态页面和动态页面分开 实现动态和静态页面负载均衡 实验5台虚拟机 一、动态负载均衡 3台虚拟机模拟: 代理服务器:30 tomcat动态页面:21、22 代理服务器: proxy_pass http://tomcat; proxy_set_h…...
基于单片机的温湿度检测及远程控制系统设计
目 录 引 言. 2 第一章 绪 论. 2 1.1 单片机简介 2 1.2 传感器简介 2 1.3 LCD液晶显示器简介 2 1.4 本设计的主要内容和目标 2 第二章 系统总体设计. 2 2.1 系统功能要求与技术指标 2 2.1.1 功能要求. 2 2.1.2 技术指标. 2 2.2 系统设计思路 2 2.3系统设计原则 2 2.4 系…...

前后端交互系统:在Node.js中运行JavaScript
在Node.js中运行JavaScript,您需要编写适用于服务器端的代码,而不是浏览器端的代码。以下是一些示例代码,用于在Node.js中创建一个简单的HTTP服务器并在浏览器中访问它: // 引入Node.js内置的http模块 const http require(http);…...

Maven学习
Maven介绍 Maven是Apache的一个开源项目,主要服务于基于Java平台的项目构建,依赖管理和项目信息管理。 Maven可以让团队能够更科学的构建项目,我们可以用配置文件的方式,对项目的名称、描述、项目版本号、项目依赖等信息进行描述…...

《动手学深度学习 Pytorch版》 10.2 注意力汇聚:Nadaraya-Watson 核回归
import torch from torch import nn from d2l import torch as d2l1964 年提出的 Nadaraya-Watson 核回归模型是一个简单但完整的例子,可以用于演示具有注意力机制的机器学习。 10.2.1 生成数据集 根据下面的非线性函数生成一个人工数据集,其中噪声项 …...

测试C#调用Windows Media Player组件
新建基于.net framework的Winform项目,可以通过添加引用的方式选择COM组件中的Windows Media Player组件,如下图所示: 也可以在VS2022的工具箱空白处点右键,选择“选择项…”菜单。 在弹出的选择工具箱项窗口中…...
面试经典150题——Day20
文章目录 一、题目二、题解 一、题目 14. Longest Common Prefix Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string “”. Example 1: Input: strs [“flower”,“flow”…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...