gitweixin
  • 首页
  • 小程序代码
    • 资讯读书
    • 工具类
    • O2O
    • 地图定位
    • 社交
    • 行业软件
    • 电商类
    • 互联网类
    • 企业类
    • UI控件
  • 大数据开发
    • Hadoop
    • Spark
    • Hbase
    • Elasticsearch
    • Kafka
    • Flink
    • 数据仓库
    • 数据挖掘
    • flume
    • Kafka
    • Hive
    • shardingsphere
    • solr
  • 开发博客
    • Android
    • php
    • python
    • 运维
    • 技术架构
    • 数据库
  • 程序员网赚
  • bug清单
  • 量化投资
  • 在线查询工具
    • 去行号
    • 在线时间戳转换工具
    • 免费图片批量修改尺寸在线工具
    • SVG转JPG在线工具

月度归档1月 2025

精品微信小程序开发门户,代码全部亲测可用

  • 首页   /  2025   /  
  • 1月
  • ( 页面2 )
运维 1月 17,2025

selenium自动化测试,谷歌驱动器chromedriver最新版本下载(亲试有效)

在用selenium做自动化测试,用的电脑后台自动更新chrome,导致chromedriver版本跟 chrome 版本不一致。

还好官方有更新最新版,可以通过下面的地址进行下载,如果没有chrome也可以在下面一起下载。

https://googlechromelabs.github.io/chrome-for-testing/

作者 east
mysql 1月 16,2025

如何在shell脚本使用 MySQL 命令行给 SQL 文件传参

在编写 Shell 脚本或进行命令行操作时,有时需要执行包含预定义 SQL 语句的文件,并且可能希望根据不同的情况向这些 SQL 文件传递参数。这在批量处理、自动化任务或动态生成查询时特别有用。MySQL 的命令行工具提供了执行 SQL 文件的能力,但直接从命令行传递参数到 SQL 文件的功能有限。因此,我们通常会结合使用文本处理工具如 sed 来实现这一点。

使用 sed 预处理 SQL 文件

sed 是一个流编辑器,可以在不打开文件的情况下对文件内容进行各种操作,比如替换文本、插入文本等。我们可以利用 sed 在执行 SQL 文件之前修改其中的占位符,将实际的参数值写入 SQL 文件中。下面是一个简单的例子:

假设有一个名为 insertstmt.sql 的 SQL 文件,其内容如下:

INSERT INTO test.demo_table VALUES(@name, @age);

这里 @name 和 @age 是我们需要从外部传递的参数。为了实现这一点,我们可以创建一个 Shell 脚本 execute.sh,它接受两个参数并用它们来替换 yoursql.sql 中的变量:

#!/bin/sh
# 设置参数作为 SQL 变量并添加到 yoursql.sql 文件的第一行
sed -i "1 i\ SET @name='$1';" yoursql.sql
sed -i "1 i\ SET @age=$2;" yoursql.sql

# 执行 SQL 脚本
mysql -u root < yoursql.sql

# 清除 SQL 脚本中的变量设置
sed -i '1,2d' yoursql.sql

在这个脚本中,$1 和 $2 分别对应于调用脚本时提供的第一个和第二个参数。当我们运行这个脚本,例如 ./execute.sh lenmom 30,它会先修改 yoursql.sql 文件,使其看起来像这样:

SET @name='lenmom';
SET @age=30;
INSERT INTO test.demo_table VALUES(@name, @age);

然后执行该 SQL 文件,最后恢复原始状态以供下次使用。

安全性与最佳实践

虽然上述方法可以很好地工作,但在实际应用中需要注意几个方面:

  • 安全性:当构建 SQL 查询字符串时,确保对外部输入进行了适当的转义,以防止 SQL 注入攻击。
  • 错误处理:考虑加入错误处理逻辑,以便在出现问题时能够及时通知用户。
  • 日志记录:记录每次操作的日志对于追踪问题和审计非常重要。
  • 备份机制:如果直接修改了 SQL 文件,在执行前最好先创建一个副本以防万一。

实际案例分析

让我们来看一个更复杂的实例,其中涉及到数据库连接信息以及多个参数的传递。想象一下,你有一个定期更新用户信息的任务,需要每天晚上更新一次特定用户的年龄信息。你可以创建一个名为 update_user_age.sh 的 Shell 脚本来完成这项任务:

#!/bin/bash
# 数据库连接信息
DB_HOST="localhost"
DB_PORT="3306"
DB_USER="root"
DB_PASS="password"

# 用户名和新年龄作为参数传递
USER_NAME="$1"
NEW_AGE="$2"

# 更新 SQL 文件中的变量
sed -i "1 i\ USE my_database;" update_user_age.sql
sed -i "2 i\ UPDATE users SET age = $NEW_AGE WHERE username = '$USER_NAME';" update_user_age.sql

# 执行更新
mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASS" < update_user_age.sql

# 恢复 SQL 文件
sed -i '1,2d' update_user_age.sql

此脚本首先指定了数据库连接的信息,接着接收用户名和新的年龄作为参数,并使用 sed 将这些参数插入到 update_user_age.sql 文件中。之后通过 MySQL 命令执行更新操作,最后清理临时添加的内容,保证 SQL 文件的干净。

作者 east
mysql 1月 16,2025

解决命令行mysql -e 执行长sql没结果

用
mysql -h${HOST_NAME} -P${PORT} -u${USER_NAME} ${DB_NAME} -e”${sql}” 方式来执行sql,发现执行没报错,但没有结果。而这个sql在navicat单独执行是没报错的,并且没有双引号等冲突。

在命令行中执行 SQL 时,如果 SQL 语句格式不正确,尤其是涉及到多行 SQL 语句或特殊字符时,命令行可能无法正确解析。可能的原因和解决办法:

  • 分隔符问题:如果 ${bigdata_charge_electric_hour} 包含多行或复杂的 SQL 语句,确保它是一个有效的单行语句。如果包含换行符或者特殊字符,命令行工具可能会解释错误。
  • 引号问题:如果 ${bigdata_charge_electric_hour} 中的 SQL 语句本身包含了引号(例如 ' 或 "),需要确保它们被正确转义,否则命令行会解析错误。可以使用双引号 " 来包装 SQL 查询,或者使用反斜杠转义其中的引号。

解决方法:如果 SQL 语句包含多行或者特殊字符,可以考虑将其写入一个 .sql 文件,并使用 mysql 命令执行文件:

mysql -h${HOST_NAME} -P${PORT} -u${USER_NAME} ${DB_NAME} < your_sql_file.sql 

这样可以避免命令行解析时的格式问题。

修改成上面的方式果然执行成功并且有结果了。

作者 east
python 1月 14,2025

如何利用大模型(ChatGPT)辅助编写selenium代码

方法1:提出需求让大模型编写

例如要编写selenium.webdriver 如何执行Esc返回键的功能 ,由于对这方面不太熟悉,如果查看官方文档,自己一步步试,可能要花费几个小时,可以把明确需求提示给大模型:

方法2:对网页某个元素进行操作

有些网页很复杂,需要对某个元素进行操作,不知代码如何写。可以先在网页上检查,在元素的页面找到要操作元素的定位,然后复制这些前端代码。告诉大模型具体需求,并找出这个元素进行操作。

例如下面的例子:

大模型提问:

selenium.webdriver如何关闭下面的组件:<div class=”container-SfTLY8″><div><div class=”coding-skill-title-container-rWvOE0 coding-skill-title-container gray-WtixXh”><div class=”skill-title-content-OwSt2V”><div class=”icon-wrapper-qgAPoM”><span role=”img” class=”semi-icon semi-icon-default”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”none” viewBox=”0 0 24 24″><path fill=”currentColor” fill-rule=”evenodd” d=”M13.113 2.266a.936.936 0 0 1 .797 1.057l-2.476 17.614a.936.936 0 0 1-1.854-.26l2.476-17.615a.936.936 0 0 1 1.057-.796M6.811 6.744a.936.936 0 0 1 0 1.324l-3.55 3.55 3.55 3.551a.936.936 0 1 1-1.324 1.324l-4.213-4.212a.936.936 0 0 1 0-1.325l4.213-4.212a.936.936 0 0 1 1.324 0m15.447 4.213c.357.356.365.93.025 1.297a6.05 6.05 0 0 0-2.377-1.001l-3.185-3.185a.936.936 0 1 1 1.324-1.324zm-3.47 10.491a.48.48 0 0 0 .48-.425c.225-1.341.423-2.03.849-2.457.425-.426 1.11-.624 2.445-.849a.485.485 0 0 0 .438-.48.48.48 0 0 0-.44-.48c-1.332-.227-2.018-.425-2.443-.851-.426-.427-.624-1.115-.849-2.455a.48.48 0 0 0-.48-.428.49.49 0 0 0-.481.426c-.226 1.341-.423 2.03-.85 2.457-.424.426-1.108.624-2.44.85a.48.48 0 0 0-.442.481c0 .26.199.448.439.48 1.335.225 2.02.418 2.444.842.426.425.623 1.114.849 2.466.04.24.23.423.482.423″ clip-rule=”evenodd”></path></svg></span></div><div class=”content-yHNqH7″><div class=”title-SlDAMe”>AI 编程</div></div><div class=”middle-jf0OPw addon-before-SEgdLZ”><div class=”flex items-center”></div></div><div class=”right-gyA7ZQ”><div><div class=”close-btn-wrapper-HINk0h” data-testid=”skill_header_close_button” tabindex=”0″ aria-describedby=”bkks5ef” data-popupid=”bkks5ef”><span role=”img” class=”semi-icon semi-icon-default close-btn-svg-_cKb_A”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”none” viewBox=”0 0 24 24″><path fill=”currentColor” d=”M5.762 18.238a.8.8 0 0 1-.234-.404 1 1 0 0 1 0-.468.85.85 0 0 1 .234-.396l4.953-4.974L5.762 7.03a.8.8 0 0 1-.226-.396 1 1 0 0 1 0-.468.84.84 0 0 1 .226-.404.86.86 0 0 1 .412-.234 1 1 0 0 1 .467 0q.233.057.404.226l4.961 4.966 4.953-4.966a.76.76 0 0 1 .404-.226.95.95 0 0 1 .46 0 .9.9 0 0 1 .419.234.84.84 0 0 1 .226.404.86.86 0 0 1 0 .468.9.9 0 0 1-.226.404l-4.953 4.958 4.953 4.966a.9.9 0 0 1 .226.412.95.95 0 0 1 0 .46.84.84 0 0 1-.226.404.86.86 0 0 1-.412.234 1 1 0 0 1-.467 0 .84.84 0 0 1-.404-.226l-4.953-4.966-4.961 4.966a.84.84 0 0 1-.404.226 1 1 0 0 1-.467 0 .86.86 0 0 1-.412-.234″></path></svg></span></div></div></div></div></div></div><div class=”editor-container-Ikyh9n”><div role=”textbox” aria-multiline=”true” autocomplete=”off” data-testid=”chat_input_input” dir=”ltr” class=”container-lGWhkH editor-h8aPs4″ data-slate-editor=”true” data-slate-node=”value” contenteditable=”true” zindex=”-1″ style=”position: relative; white-space: pre-wrap; overflow-wrap: break-word; min-height: 28px;”><div data-slate-node=”element” class=”paragraph-GvBwll”><span data-slate-node=”text”><span data-slate-leaf=”true” style=”padding-left: 0.1px;”><span data-slate-placeholder=”true” contenteditable=”false” style=”position: absolute; top: 0px; pointer-events: none; width: 100%; max-width: 100%; display: block; opacity: 0.333; user-select: none; text-decoration: none;”>粘贴代码或描述你的问题</span><span data-slate-zero-width=”n” data-slate-length=”0″><br></span></span></span></div></div></div><div><div class=”skill-bottom-X9vRY0″><div class=”flex-1 flex”><div class=”btn-vRbV5q mr-10″><div class=”upload-btn-wrapper-h2mSyQ”><div class=”” aria-haspopup=”true” tabindex=”0″ aria-describedby=”15ykpw2″ data-popupid=”15ykpw2″><button class=”semi-button semi-button-primary samantha-button-aiwXEk tertiary-kH1mvr medium-UIYozi icon-a2WxRL semi-button-with-icon” type=”button” aria-disabled=”false”><span class=”semi-button-content”><span role=”img” class=”semi-icon semi-icon-default !text-16 text-s-color-text-secondary”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”none” viewBox=”0 0 24 24″><path fill=”currentColor” fill-rule=”evenodd” d=”M9.035 15.956a1.29 1.29 0 0 0 1.821-.004l6.911-6.911a3.15 3.15 0 0 0 0-4.457l-.034-.034a3.15 3.15 0 0 0-4.456 0l-7.235 7.234a5.031 5.031 0 0 0 7.115 7.115l6.577-6.577a1.035 1.035 0 0 1 1.463 1.464l-6.576 6.577A7.1 7.1 0 0 1 4.579 10.32l7.235-7.234a5.22 5.22 0 0 1 7.382 0l.034.034a5.22 5.22 0 0 1 0 7.383l-6.91 6.91a3.36 3.36 0 0 1-4.741.012l-.006-.005-.012-.011a3.346 3.346 0 0 1 0-4.732L12.76 7.48a1.035 1.035 0 0 1 1.464 1.463l-5.198 5.198a1.277 1.277 0 0 0 0 1.805z” clip-rule=”evenodd”></path></svg></span><span class=”semi-button-content-right” x-semi-prop=”children”><span style=”color: rgba(0, 0, 0, 0.8);”>上传文件</span></span></span></button></div></div><input type=”file” multiple=”” class=”input-Er4iXs”><input type=”file” directory=”true” mozdirectory=”true” webkitdirectory=”true” class=”input-Er4iXs”></div><div class=”btn-vRbV5q”><button class=”semi-button semi-button-primary samantha-button-aiwXEk tertiary-kH1mvr medium-UIYozi icon-a2WxRL upload-github-svg-oE359X semi-button-with-icon” type=”button” aria-disabled=”false”><span class=”semi-button-content”><span role=”img” class=”semi-icon semi-icon-default text-20 text-s-color-text-secondary”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”none” viewBox=”0 0 24 24″><path fill=”currentColor” d=”M18.608 5.027a.83.83 0 0 0-.444-.459c-.17-.073-1.674-.65-3.912.765H9.748c-2.241-1.418-3.744-.838-3.912-.765a.84.84 0 0 0-.444.459c-.512 1.293-.353 2.665-.154 3.455-.616.823-.738 1.586-.738 2.685 0 3.474 2.483 4.45 4.428 4.81a3.7 3.7 0 0 0-.254.909 1 1 0 0 0-.007.114v.754c-.381-.08-.801-.233-1.078-.51-.939-.94-1.91-1.91-3.089-1.91V17c.488 0 1.277.788 1.91 1.422.997.997 2.522 1.08 2.997 1.08q.092 0 .127-.003a.834.834 0 0 0 .8-.832v-1.604c.083-.483.394-.969.545-1.1a.833.833 0 0 0-.451-1.457c-2.699-.308-4.261-.954-4.261-3.34 0-1.037.11-1.381.616-1.939a.84.84 0 0 0 .156-.875c-.094-.232-.367-1.278-.127-2.295.404-.031 1.17.089 2.221.8.137.093.3.143.467.143h5c.167 0 .33-.05.467-.143 1.056-.714 1.816-.816 2.22-.804.241 1.02-.03 2.066-.125 2.299a.83.83 0 0 0 .157.877c.505.553.614.898.614 1.938 0 2.394-1.561 3.037-4.259 3.338a.834.834 0 0 0-.451 1.46c.386.332.544.633.544 1.035v2.5h1.666V17c0-.4-.076-.744-.19-1.038 1.895-.373 4.357-1.39 4.357-4.795 0-1.101-.122-1.866-.738-2.686.2-.79.357-2.159-.154-3.454″></path></svg></span><span class=”semi-button-content-right” x-semi-prop=”children”><span>引入 GitHub 仓库</span></span></span></button></div></div><div class=””><div class=”container-G7o3Dk”><div class=””><div class=”flex items-center”><div class=”left-area-UbioLp”><div class=”flex gap-8″><div class=”container-KtUMxI”><div data-testid=”asr_btn” class=”mic-container-uZJexl cursor-pointer” tabindex=”0″ aria-describedby=”30rnba0″ data-popupid=”30rnba0″><span role=”img” class=”semi-icon semi-icon-default icon-mic-NfScWD”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”none” viewBox=”0 0 24 24″><path fill=”currentColor” fill-rule=”evenodd” d=”M12 2.5A2.5 2.5 0 0 1 14.5 5v6a2.5 2.5 0 0 1-5 0V5A2.5 2.5 0 0 1 12 2.5M7.5 5a4.5 4.5 0 0 1 9 0v6a4.5 4.5 0 1 1-9 0zm-2 4a1 1 0 0 0-2 0v2c0 4.213 3.26 7.928 7.5 8.44V21H7.3a1 1 0 1 0 0 2h9.4a1 1 0 1 0 0-2H13v-1.56c4.24-.512 7.5-4.227 7.5-8.44V9a1 1 0 1 0-2 0v2c0 3.46-2.915 6.5-6.5 6.5S5.5 14.46 5.5 11z” clip-rule=”evenodd”></path></svg></span></div></div></div></div><div class=”splitLineWrapper-bVwctz”><div class=”splitLine-wCW4fL”></div></div><div class=”right-area-YZHkCa”><div class=”container-F3bkhm !hidden” data-testid=”chat_input_local_break_button” tabindex=”0″ aria-describedby=”vkk33zj” data-popupid=”vkk33zj”><span role=”img” class=”semi-icon semi-icon-default icon-eivsqq”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”none” viewBox=”0 0 24 24″><path fill=”currentColor” fill-rule=”evenodd” d=”M12 23c6.075 0 11-4.925 11-11S18.075 1 12 1 1 5.925 1 12s4.925 11 11 11m0-20a9 9 0 1 1 0 18 9 9 0 0 1 0-18m-2 5.5A1.5 1.5 0 0 0 8.5 10v4a1.5 1.5 0 0 0 1.5 1.5h4a1.5 1.5 0 0 0 1.5-1.5v-4A1.5 1.5 0 0 0 14 8.5z” clip-rule=”evenodd”></path></svg></span></div><div class=”container-e6cFq5 send-btn-wrapper coding-send-msg-button”><span class=”” tabindex=”0″ aria-describedby=”5u16go3″ data-popupid=”5u16go3″ style=”display: inline-block; cursor: not-allowed;”><button disabled=”” id=”flow-end-msg-send” aria-disabled=”true” aria-label=”发送” data-testid=”chat_input_send_button” class=”semi-button semi-button-disabled semi-button-primary-disabled send-btn-S_MjUh semi-button-with-icon semi-button-with-icon-only” type=”button” style=”pointer-events: none;”><span class=”semi-button-content”><span role=”img” class=”semi-icon semi-icon-default”><svg xmlns=”http://www.w3.org/2000/svg” width=”1em” height=”1em” fill=”currentColor” viewBox=”0 0 28 28″><path d=”M14.007 28q-2.881 0-5.418-1.11a14.3 14.3 0 0 1-4.453-3.026 14.5 14.5 0 0 1-3.04-4.453Q0 16.875 0 13.993q0-2.868 1.095-5.404Q2.191 6.053 4.121 4.12A14.5 14.5 0 0 1 8.59 1.095Q11.124 0 13.993 0t5.404 1.095q2.536 1.096 4.467 3.026A14.2 14.2 0 0 1 26.89 8.59Q28 11.124 28 13.993q0 2.881-1.095 5.418a14.3 14.3 0 0 1-3.027 4.453 14.2 14.2 0 0 1-4.467 3.026Q16.876 28 14.007 28m.015-6.917q.518 0 .835-.317.332-.332.332-.836v-6.816l-.13-2.969 1.37 1.672 1.599 1.643q.316.33.807.331.475 0 .792-.303a.99.99 0 0 0 .332-.763q0-.475-.332-.807L14.944 7.22q-.433-.447-.922-.447-.476 0-.908.447L8.43 11.918q-.317.317-.317.807 0 .461.317.763.318.303.778.303.49 0 .807-.331l1.629-1.658 1.326-1.642-.116 2.954v6.816q0 .504.332.836.331.317.836.317″></path></svg></span></span></button></span></div></div></div></div></div></div></div></div></div>

大模型回答:

要使用 Selenium WebDriver 关闭特定的组件(例如,带有类名 .close-btn-wrapper-HINk0h 的关闭按钮),可以通过定位该元素并模拟点击操作来实现。以下是详细的步骤和示例代码:

步骤:

  1. 导入必要的模块:
    • webdriver 用于控制浏览器。
    • By 用于元素定位。
    • time (可选)用于添加延时,以确保元素已加载。
  2. 初始化 WebDriver:
    • 以 Chrome 为例,确保已安装相应的 WebDriver 并配置好环境。
  3. 打开目标网页:
    • 使用 driver.get() 方法导航到目标 URL。
  4. 定位关闭按钮:
    • 使用合适的定位策略(如 By.CLASS_NAME、By.CSS_SELECTOR 或 By.XPATH)来定位关闭按钮。
  5. 模拟点击操作:
    • 使用 click() 方法点击关闭按钮,以关闭组件。
  6. 关闭浏览器:
    • 使用 driver.quit() 方法关闭浏览器窗口并释放资源。

示例代码:

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

# 初始化 WebDriver(以 Chrome 为例)
driver = webdriver.Chrome()

try:
    # 打开目标网页
    driver.get('https://www.example.com')

    # 等待页面加载(可根据需要调整时间或使用显式等待)
    time.sleep(2)

    # 定位关闭按钮
    # 方法一:使用 CSS 选择器
    close_button = driver.find_element(By.CSS_SELECTOR, '.close-btn-wrapper-HINk0h [role="img"]')
    
    # 方法二:使用 XPath
    # close_button = driver.find_element(By.XPATH, '//div[@class="close-btn-wrapper-HINk0h"]//*[@role="img"]')

    # 模拟点击关闭按钮
    close_button.click()

    # 等待观察效果(可选)
    time.sleep(2)

finally:
    # 关闭浏览器
    driver.quit()

说明:

  1. 定位策略:
    • 在示例中,使用了 CSS 选择器 .close-btn-wrapper-HINk0h [role="img"] 来定位关闭按钮。这个选择器首先定位具有类名 .close-btn-wrapper-HINk0h 的 div,然后在该 div 下查找具有 role="img" 属性的子元素,这通常是实际的关闭图标。
    • 你也可以使用 XPath 来定位,如注释中的方法二所示。
  2. 等待元素加载:
    • 使用 time.sleep(2) 是一种简单的等待方式,但在实际项目中,建议使用显式等待(WebDriverWait)来提高脚本的稳定性和效率。例如:from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待关闭按钮可点击 close_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '.close-btn-wrapper-HINk0h [role="img"]')) ) close_button.click()
  3. 异常处理:
    • 在实际应用中,建议添加异常处理,以应对元素未找到或其他潜在问题。例如:try: close_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '.close-btn-wrapper-HINk0h [role="img"]')) ) close_button.click() except Exception as e: print(f"无法关闭组件: {e}")
  4. 确保元素可交互:
    • 确保在尝试点击关闭按钮之前,该按钮处于可见且可点击的状态。如果按钮被其他元素遮挡或尚未完全加载,点击操作可能会失败。
作者 east
doris 1月 14,2025

解决Navicat查看导入运行任务ROUTINE:1105 – errCode = 2, detailMessage = Please check your sql, we meet an error when parsing.

第一次 通过 Doris Routine Load 导入方式持续消费 Kafka Topic 中的数据。 按官方文档
Routine Load – Apache Doris

在navicat按照官方示例查看导入任务:

mysql> SHOW ROUTINE LOAD FOR testdb.example_routine_load\G

发现报错:

1105 – errCode = 2, detailMessage = Please check your sql, we meet an error when parsing.

刚开始很郁闷,以为doris官方团队开发的功能很敷衍,没具体提示具体是什么错误。

反复试验之后,才发现是\G 导致报错,修改为下面这样就正常了

mysql> SHOW ROUTINE LOAD FOR testdb.example_routine_load
作者 east
python 1月 14,2025

selenium.webdriver 如何执行Esc返回键的功能(python实现)

在使用 Selenium WebDriver 进行自动化测试时,有时需要模拟键盘操作,例如按下 Esc 键来执行返回或取消操作。Selenium 提供了多种方法来实现这一点,以下是几种常见的方法:

方法一:使用 send_keys 方法

send_keys 方法允许你向当前聚焦的元素发送按键事件。要模拟按下 Esc 键,可以使用 Keys.ESCAPE。

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

# 初始化 WebDriver(以 Chrome 为例)
driver = webdriver.Chrome()

try:
    # 打开目标网页
    driver.get('https://www.example.com')

    # 等待页面加载
    time.sleep(2)

    # 向当前聚焦的元素发送 Esc 键
    driver.find_element(By.TAG_NAME, 'body').send_keys(Keys.ESCAPE)

    # 或者,如果你知道具体的元素,可以定位后发送
    # element = driver.find_element(By.ID, 'some-element-id')
    # element.send_keys(Keys.ESCAPE)

    # 等待观察效果
    time.sleep(2)

finally:
    # 关闭浏览器
    driver.quit()

方法二:使用 ActionChains

ActionChains 提供了更复杂的用户交互方式,包括键盘操作。你可以使用 send_keys 方法来发送 Esc 键。

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time

# 初始化 WebDriver(以 Chrome 为例)
driver = webdriver.Chrome()

try:
    # 打开目标网页
    driver.get('https://www.example.com')

    # 创建 ActionChains 对象
    actions = ActionChains(driver)

    # 向当前聚焦的元素发送 Esc 键
    actions.send_keys(Keys.ESCAPE).perform()

    # 或者,指定某个元素
    # element = driver.find_element(By.ID, 'some-element-id')
    # actions.move_to_element(element).send_keys(Keys.ESCAPE).perform()

    # 等待观察效果
    time.sleep(2)

finally:
    # 关闭浏览器
    driver.quit()

方法三:使用 JavaScript 执行(适用于某些特定场景)

在某些情况下,直接使用 JavaScript 来触发 Esc 键的效果可能更为有效,尤其是当涉及到模态窗口、弹出层等需要关闭的 UI 元素时。

from selenium import webdriver
import time

# 初始化 WebDriver(以 Chrome 为例)
driver = webdriver.Chrome()

try:
    # 打开目标网页
    driver.get('https://www.example.com')

    # 等待页面加载
    time.sleep(2)

    # 使用 JavaScript 触发 Esc 键的效果
    driver.execute_script("document.activeElement.blur();")  # 失去焦点
    driver.execute_script("window.dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', code: 'Escape', bubbles: true}));")

    # 等待观察效果
    time.sleep(2)

finally:
    # 关闭浏览器
    driver.quit()

注意事项

  1. 等待元素加载:在执行键盘操作之前,确保目标元素已经加载并且可以被操作。可以使用显式等待(WebDriverWait)来代替 time.sleep,以提高脚本的稳定性和效率。
  2. 元素定位:确保你定位的元素是当前聚焦的元素,或者明确指定要发送 Esc 键的元素。
  3. 浏览器兼容性:不同浏览器对键盘事件的处理可能略有不同,确保在目标浏览器上进行测试。
  4. 权限和安全性:某些网站可能会检测到自动化工具并阻止其操作,确保遵守网站的使用条款,并在合法范围内使用自动化测试。
作者 east
Android 1月 12,2025

WindowManagerService(WMS)的性能优化与调试

一、性能优化策略

(一)窗口重绘优化

  • 减少不必要的重绘
    • WMS 会智能地跟踪窗口的状态变化,只有当窗口的属性发生实质性改变时,才会触发重绘操作。例如,当窗口的背景颜色发生变化,但窗口内的视图布局和内容没有改变时,WMS 不会对整个窗口进行重新绘制。它会通过内部的机制,如判断视图的脏区域(dirty region),只更新背景部分,这样可以大大减少绘制的工作量。
    • 对于复杂的窗口,包含多个子视图的情况,这种优化更为重要。例如,在一个包含列表视图和头部视图的窗口中,如果只是列表中的某一项内容发生变化,WMS 会尽量只重绘该项对应的视图区域,而不是整个列表视图,从而节省系统资源,提高绘制效率。
  • 采用缓冲技术
    • WMS 采用双缓冲或三缓冲技术来优化窗口的绘制操作,减少绘制过程中的闪烁和卡顿现象。双缓冲技术就像是有两块画布,一块在前台显示,另一块在后台绘制。当后台的画布绘制完成后,再将其与前台的画布进行交换,这样可以避免在绘制过程中用户看到不完整的画面而产生闪烁感。
    • 三缓冲技术则是在双缓冲的基础上增加了一块备用画布,进一步提高了绘制的效率和流畅性。例如,在高帧率的游戏应用或者快速滚动的列表视图中,缓冲技术能够有效地保证画面的平滑过渡,提供更好的视觉体验。

(二)资源分配优化

  • 多窗口环境下的资源平衡
    • 在多窗口环境下,WMS 会合理地分配系统资源,避免因某个窗口占用过多资源而导致其他窗口的性能下降。它会根据窗口的优先级、类型以及用户的操作焦点等因素来分配资源。例如,当系统资源紧张时,WMS 会降低一些非关键窗口(如后台运行的小工具窗口)的帧率,优先保证关键窗口(如正在播放视频的窗口或用户正在交互的窗口)的性能。
    • 对于不同类型的窗口,WMS 也有不同的资源分配策略。比如,对于输入法窗口,会确保其响应的及时性,以便用户能够流畅地输入文字;对于系统警报窗口,会优先保证其显示的优先级,使其能够及时引起用户的注意。
  • 内存管理优化
    • WMS 会对窗口相关的内存资源进行有效的管理。在窗口创建时,它会合理地分配内存给窗口的视图层次结构、Surface 等对象。当窗口不再需要时,会及时释放这些内存资源。例如,在一个 Activity 窗口被销毁后,WMS 会通过一系列的机制,如释放与该窗口关联的 Surface 对象所占用的内存,回收窗口状态对象(WindowState)占用的内存等,确保内存的有效利用,减少内存泄漏的风险。

二、调试工具与方法

(一)使用 adb 命令进行调试

  • 查看窗口信息
    • 通过adb shell dumpsys window命令可以获取 WMS 的运行状态信息,特别是窗口相关的详细信息。这个命令会输出当前系统中所有窗口的类型、层级、大小、位置等信息。例如,开发人员可以通过这个命令查看一个应用程序的所有窗口是否按照预期的层级和位置显示。如果发现某个窗口的层级异常,可能是窗口管理的逻辑出现了问题。
    • 还可以使用adb shell dumpsys window <window_name>命令来查看特定窗口的详细信息。这对于排查某个特定窗口的问题非常有用,比如一个自定义的对话框窗口没有正确显示或者无法接收输入事件时,可以通过这个命令查看其详细的属性和状态。
  • 跟踪事件处理过程
    • 利用adb命令结合系统日志,可以跟踪 WMS 的事件处理过程。例如,通过设置日志标签(log tag)并使用adb logcat命令来查看与窗口管理相关的日志信息。当窗口被创建、更新或者销毁时,相关的日志会记录这些事件的详细过程,包括时间戳、操作类型、涉及的窗口等信息。这有助于开发人员了解 WMS 是如何处理这些事件的,以及在事件处理过程中是否出现了异常情况。

(二)利用系统日志进行调试

  • 分析工作流程异常
    • 系统日志是一个重要的调试工具,它记录了 WMS 的工作流程和事件处理过程。当出现窗口显示异常、输入事件分发错误等问题时,可以通过查看系统日志来查找线索。例如,如果一个窗口无法接收触摸事件,在系统日志中可能会发现是因为该窗口的焦点状态没有正确设置或者输入事件分发的逻辑出现了错误。
    • 系统日志还会记录一些错误信息和警告信息,这些信息对于定位和解决性能问题也非常有帮助。比如,当窗口重绘过于频繁导致系统性能下降时,系统日志可能会记录相关的警告信息,提示开发人员检查窗口更新的逻辑是否合理。
  • 结合日志级别进行调试
    • Android 系统的日志分为不同的级别,如 DEBUG、INFO、WARN、ERROR 等。开发人员可以根据需要调整日志级别,以便获取更详细或者更关键的信息。在开发和调试阶段,可以将日志级别设置为 DEBUG,获取最详细的日志信息,包括 WMS 内部的一些调试信息和状态变化。在产品发布阶段,可以将日志级别适当提高,只记录重要的警告和错误信息,以减少日志对系统性能的影响。

(三)使用 Android Studio 的调试工具

  • Layout Inspector 工具
    • Android Studio 中的 Layout Inspector 工具可以查看当前窗口的布局结构和属性。通过这个工具,开发人员可以直观地看到窗口内各个视图的层次结构、大小、位置以及它们的属性设置。例如,当窗口的布局出现问题,如视图重叠或者位置偏移时,可以使用这个工具来快速定位问题所在的视图层次,检查视图的布局参数是否正确设置。
    • 它还可以帮助开发人员分析窗口的性能问题。比如,如果一个窗口的布局过于复杂,导致加载时间过长或者绘制性能下降,可以通过 Layout Inspector 工具来查看布局结构,寻找优化的空间,如减少不必要的视图嵌套或者优化视图的大小和位置计算方式。
  • Profiler 工具
    • Profiler 工具可以用于分析应用程序的性能,包括与 WMS 相关的性能问题。它可以监测 CPU、内存、GPU 等资源的使用情况,以及帧率、卡顿等性能指标。当发现窗口显示卡顿或者系统整体性能下降时,可以使用 Profiler 工具来查看 WMS 相关的操作是否占用了过多的资源。
    • 例如,如果发现 WMS 在窗口重绘过程中占用了大量的 CPU 资源,可以通过 Profiler 工具进一步分析重绘的具体过程,找出导致性能问题的具体代码段或者操作,如复杂的绘图逻辑或者频繁的视图更新,从而进行针对性的优化。
作者 east
Android 1月 12,2025

Android Framework WMS与其他系统服务的协作

在 Android 系统这个复杂而精密的体系中,WMS(WindowManagerService)并不是独立工作的,它与其他系统服务紧密协作,共同实现了系统的高效运行和丰富的用户体验。

一、WMS 与 AMS(Activity Manager Service)的协作

(一)窗口创建阶段

  • 交互流程:当 AMS 需要创建一个新的 Activity 窗口时,会通过 Binder 机制调用 WMS 的 addView 方法。这就好比 AMS 是一个建筑设计师,规划好了要建一座新的 “房子”(Activity 窗口),然后把这个任务交给 WMS 这个 “建筑工人” 去实际建造。
  • 资源分配与协作细节:WMS 接收到请求后,会创建相应的 WindowState 对象,并为其分配 Surface 资源。Surface 资源就像是房子的 “墙面”,是窗口进行内容绘制的基础。例如,在创建一个地图应用的 Activity 窗口时,WMS 会为其分配足够的 Surface 资源,以确保地图能够完整、清晰地显示在窗口中。

(二)窗口布局阶段

  • 信息传递与协作方式:WMS 负责计算窗口的大小和位置,这是基于系统配置(如屏幕分辨率、方向等)以及窗口本身的属性(如窗口类型、内容大小等)来进行的。然后,WMS 会通过回调 AMS 的 onConfigurationChanged 方法通知应用程序配置更改。这就像是 WMS 完成了房间的布局规划后,把布局信息告诉 AMS,让 AMS 去通知房间的 “主人”(应用程序)房间的格局发生了变化。
  • 对用户体验的影响:这种协作确保了应用程序能够及时调整界面布局,以适应窗口大小和位置的变化。例如,在屏幕方向发生改变时,应用程序可以根据 WMS 提供的新布局信息,重新排列界面元素,如将竖屏模式下的列表布局调整为横屏模式下的平铺布局,从而提供更好的用户体验。

(三)焦点管理阶段

  • 焦点更新机制:WMS 维护着焦点窗口的列表,这个焦点窗口就是当前正在与用户交互的窗口,比如用户正在输入文字的编辑窗口或者正在点击操作的游戏窗口等。当焦点发生变化时,WMS 会通知 AMS 更新 Activity 的焦点状态。
  • 对应用程序的作用:这使得应用程序能够准确地知道哪个窗口处于焦点状态,从而正确地处理用户输入。例如,在一个聊天应用中,当用户从聊天列表窗口切换到聊天输入窗口时,WMS 会通知 AMS 焦点的转移,聊天输入窗口所在的 Activity 就可以激活输入法,准备接收用户的输入内容。

(四)动画处理阶段

  • 协作实现动画效果:WMS 负责实现窗口切换动画,比如淡入淡出、滑动等动画效果。在进行窗口切换动画时,WMS 会通知 AMS 暂停或恢复 Activity 的绘制。这就像是一场舞台表演,WMS 是舞台特效师,负责窗口切换的动画特效,而 AMS 则像是演员调度员,根据特效师的要求安排演员(Activity)暂停或继续表演。
  • 确保动画平滑过渡的重要性:这种协作确保了动画的平滑过渡,避免了窗口切换过程中的画面闪烁或卡顿。例如,在打开一个新的应用程序时,旧应用程序窗口逐渐淡出,新应用程序窗口逐渐淡入,用户可以看到一个流畅的过渡过程,提升了系统的视觉体验。

(五)输入事件分发阶段

  • 不同类型窗口的事件分发方式:WMS 作为输入事件的中转站,会将接收到的事件分发给相应的窗口。对于非装饰窗口(如 PopupWindow),WMS 会直接处理事件;而对于装饰窗口(如 Activity),WMS 会将事件传递给 AMS,由 AMS 再转发给具体的 Activity。这就好比信件的投递,WMS 是邮局,先对信件(输入事件)进行分类,然后把属于 Activity 的信件交给 AMS,由 AMS 送到具体的 “收件人”(Activity)手中。
  • 保障输入事件的准确处理:这种分工明确的协作方式确保了输入事件能够准确地被相应的窗口处理。例如,当用户在屏幕上点击一个按钮时,WMS 会根据按钮所在的窗口类型,正确地将点击事件发送到对应的窗口处理逻辑中,保证了应用程序能够按照用户的意图做出正确的响应。

二、WMS 与 SurfaceFlinger 的协作

  • 显示控制与渲染协作流程:WMS 通过创建 SurfaceControl 对象并与 SurfaceFlinger 交互,实现了窗口的显示和更新。SurfaceControl 作为 SurfaceFlinger 的核心组件,负责管理窗口的显示层级和属性。例如,WMS 利用 SurfaceControl 的 setLayer 方法设置窗口的 Z 轴层级,就像在一个多层的展示架上安排物品的位置一样,确保正确的显示顺序。
  • 对图形显示的重要性:这种协作机制实现了高效的窗口渲染和管理,为 Android 系统提供了流畅的用户界面体验。SurfaceFlinger 就像是一个图形合成器,将各个窗口的内容(通过 Surface)进行合成,然后显示在屏幕上。WMS 与 SurfaceFlinger 的紧密配合,使得窗口能够按照正确的顺序、位置和属性显示出来,无论是简单的文本窗口还是复杂的游戏画面,都能准确地呈现在用户眼前。比如在一个多窗口应用场景中,WMS 和 SurfaceFlinger 协同工作,保证不同窗口的内容能够正确地叠加和显示,不会出现窗口内容混乱的情况。
作者 east
Android 1月 12,2025

WindowManagerService 类的工作流程详细介绍

一、窗口创建流程

  1. 请求接收
    • 当应用程序(通过 Activity 或者其他窗口组件)需要创建一个新窗口时,会通过 WindowManager 接口向 WMS 发送请求。这个请求会通过 Binder 机制传递到 WindowManagerService 类。例如,在 Activity 的 onCreate 方法中调用 setContentView 方法,最终会触发这个创建窗口的请求。
    • 此时,WMS 就像一个窗口 “接待员”,开始处理这个请求。它会先检查请求的合法性,确保只有符合系统安全和策略要求的窗口才能被创建。
  2. 权限验证
    • WMS 通过 mPolicy.checkAddPermission 方法进行权限验证。这一步就像是在门口设置的 “安检关卡”,只有通过权限验证的窗口请求才能进入下一步的创建流程。如果权限验证失败,例如应用程序没有足够的权限创建特定类型的窗口,那么将立即终止添加过程,防止非法或不符合系统规定的窗口出现。
  3. 创建 WindowToken(部分情况)
    • 对于一些系统窗口(如输入法窗口),需要主动创建 WindowToken。这通常由特定的服务(如 InputMethodManagerService)来完成。WindowToken 就像是窗口的 “身份证”,用于标识窗口所属的应用程序或任务。在创建窗口的过程中,这个步骤确保了窗口身份的唯一性和安全性。
  4. 创建 WindowState 实例
    • WMS 为新窗口创建 WindowState 实例。WindowState 是 WMS 中表示窗口状态的核心对象,负责管理窗口的各种属性和状态,如位置、大小、透明度等。这个实例就像是为新窗口建立的一个 “信息档案”,记录了窗口从创建到销毁过程中的所有重要信息。
  5. 添加到 DisplayContent
    • WMS 将新创建的 WindowState 添加到相应的 DisplayContent 中。DisplayContent 负责管理特定物理显示设备上的所有窗口信息,它就像是一个 “窗口仓库”,将不同的窗口按照一定的规则存储起来。这样可以方便 WMS 对窗口进行管理,特别是在处理多屏幕或者多窗口场景时,能够有效地组织和管理窗口的显示。
  6. 布局计算
    • WMS 会根据窗口的属性(如类型、大小要求等)和系统的配置(如屏幕分辨率、方向等)计算窗口的布局信息,包括位置、大小等。这一步就像是一个 “空间规划师”,为窗口在屏幕上找到最合适的 “居住空间”,确保窗口能在屏幕上正确显示。例如,对于一个普通的应用程序窗口,它会根据屏幕的可用空间和窗口的优先级,计算出一个合适的位置和大小,使得窗口既能够完整显示内容,又不会与其他窗口产生冲突。
  7. 创建 SurfaceControl
    • WMS 为窗口创建 SurfaceControl 对象。SurfaceControl 是 SurfaceFlinger 用于管理窗口显示的核心组件,它就像是一个 “显示控制器”,负责管理窗口的显示层级和属性。通过创建 SurfaceControl,WMS 为窗口的显示做好了准备工作,将窗口的显示控制权交给了 SurfaceControl。
  8. 提交 SurfaceControl
    • WMS 将创建好的 SurfaceControl 提交给 SurfaceFlinger。SurfaceFlinger 是 Android 系统中负责图形合成和显示的重要组件,它就像一个 “舞台导演”,将各个窗口的内容组合在一起并显示在屏幕上。这一步骤完成了窗口的实际显示准备,使得窗口的内容能够通过 SurfaceFlinger 最终呈现在屏幕上。
  9. 完成绘制与显示窗口
    • WMS 调用 WindowState 的 finishDraw 方法,完成窗口的绘制过程。这标志着窗口在内容绘制方面已经准备就绪。然后,WMS 通过调用 WindowState 的 prepareSurface 方法,最终将窗口显示在屏幕上。此时,用户就可以看到新创建的窗口出现在屏幕上了。

二、窗口更新流程

  1. 请求接收与权限验证
    • 当应用程序通过 WindowManager 接口发出更新窗口的请求时,WMS 首先会接收这个请求。然后,它会像创建窗口时一样,通过验证权限来确保请求的合法性。只有通过权限验证的请求,才能进行后续的窗口更新操作。这一步是为了防止未经授权的应用程序对窗口进行非法修改。
  2. 获取目标 WindowState
    • 经过权限验证后,WMS 会获取目标 WindowState。因为 WindowState 记录了窗口的所有状态信息,所以 WMS 需要通过这个对象来更新窗口的相关属性。这就好比要修改一个人的档案信息,首先要找到对应的档案一样。
  3. 更新 WindowState 字段
    • 根据新的属性值,WMS 更新 WindowState 的相关字段。例如,如果窗口的大小发生了变化,WMS 会更新 WindowState 中的 width 和 height 属性;如果位置发生了变化,则更新 x 和 y 坐标属性。这些更新后的属性信息会被用于后续的显示调整。
  4. 调整窗口层级(如有必要)
    • 当窗口的更新涉及到层级变化时,例如窗口的 Z – Order 值改变,WMS 会调整窗口在层级树中的位置。这是为了确保窗口之间的正确叠加关系,保证窗口的显示顺序符合用户的预期和系统的规则。
  5. 通知 SurfaceFlinger 重新绘制
    • 最后,WMS 通知 SurfaceFlinger 重新绘制窗口。因为窗口的属性发生了变化,所以需要重新绘制窗口的内容,以反映这些变化。SurfaceFlinger 会根据 WMS 提供的新的窗口属性和内容信息,重新合成和显示窗口,使得用户能够看到更新后的窗口效果。

三、窗口销毁流程

  1. 应用端触发与请求发送
    • 通常在 Activity 的 onDestroy () 方法中开始窗口的移除过程。这是因为当 Activity 结束时,与之相关的窗口也不再需要显示。应用程序会通过 IWindowSession 接口向 WMS 发送移除请求,这个接口就像是应用程序和 WMS 之间的 “通信管道”,负责传递移除窗口的请求。
  2. WMS 处理与 Surface 资源释放
    • WMS 接收到请求后,会调用 WindowState 的 removeIfPossible () 方法,尝试移除窗口。同时,它会释放与窗口关联的 Surface 资源。Surface 资源的释放就像是清理一块用过的画布,避免资源浪费,为系统腾出更多的资源用于其他操作。
  3. 状态更新与容器清理
    • WMS 会更新 WindowState 的 mRemoveFlags 标志位,标记窗口已被移除。然后,从 DisplayContent 中移除窗口,清理窗口在层级树中的位置。这就好比把一个已经处理完的物品从仓库的货架上移除,使得仓库(DisplayContent)的空间得到合理利用,并且系统的窗口管理结构能够保持整洁和高效。
作者 east
Android 1月 12,2025

WindowManagerService(WMS)关键类详细介绍

一、WindowManagerService 类

  • 核心地位与职责概述
    • WindowManagerService 类是 WMS 的主要实现类,就像整个窗口管理系统的大脑,处于绝对的核心地位。它承担着窗口管理的诸多关键职责,包括窗口的创建、管理和销毁等一系列核心功能。
    • 它就像是一个繁忙的交通指挥中心,接收来自各个应用程序的请求,然后有条不紊地指挥各个组件协同工作,确保窗口能够正常地显示和操作。
  • 与其他组件的交互关系
    • 和客户端应用程序:通过 Binder 机制接收应用程序发送的请求。例如,当应用程序想要创建一个新窗口时,会通过 WindowManager 接口向 WMS 发送请求,这个请求最终会到达 WindowManagerService 类进行处理。
    • 和 WindowState 类:在创建窗口时,会为每个窗口创建一个 WindowState 实例,用于跟踪和管理窗口的各种状态信息。它和 WindowState 类紧密协作,通过调用 WindowState 的方法来实现对窗口状态的更新,如窗口位置、大小的改变等。
    • 和 SurfaceFlinger:通过创建 SurfaceControl 对象与 SurfaceFlinger 进行交互。在窗口显示过程中,它将 SurfaceControl 提交给 SurfaceFlinger,使得窗口能够在屏幕上显示出来。而且,当窗口属性发生变化需要更新显示时,它也会通知 SurfaceFlinger 进行重新绘制。
  • 关键方法解析
    • addView 方法:这是创建窗口的关键方法之一。当 AMS(Activity Manager Service)需要创建一个新的 Activity 窗口或者应用程序通过 WindowManager 请求添加一个视图窗口时,会调用这个方法。它首先会进行权限验证,然后创建 WindowState 和 SurfaceControl 等相关对象,完成窗口的创建和添加流程。例如,在应用程序启动一个新的 Activity 时,系统会通过这个方法为 Activity 创建窗口并添加到系统的窗口管理体系中。
    • removeView 方法:用于销毁窗口。当应用程序结束或者窗口不再需要时,会调用这个方法。它会释放与窗口相关的各种资源,包括 Surface 资源,更新窗口状态,并从 DisplayContent 中移除窗口相关信息。例如,在 Activity 的 onDestroy 方法中,就会触发这个方法来销毁窗口。
    • updateViewLayout 方法:当窗口的布局信息(如位置、大小等属性)发生变化时,应用程序会通过这个方法向 WMS 发送更新请求。它会获取对应的 WindowState 对象,更新其布局相关的字段,并根据需要调整窗口在层级树中的位置,最后通知 SurfaceFlinger 重新绘制窗口,以实现窗口布局的更新。

二、WindowState 类

  • 状态信息存储与管理角色
    • WindowState 类主要负责存储和管理窗口的各种状态信息,是窗口在 WMS 中的 “代言人”。它记录了窗口的属性,如位置(通过 x 和 y 坐标)、大小(width 和 height)、透明度等,就像是一个窗口的信息档案库。
    • 除了基本属性,它还跟踪窗口的生命周期,从创建、显示、隐藏到销毁的各个阶段,都有相应的状态记录和处理机制。例如,它可以知道一个窗口当前是处于显示状态还是隐藏状态,以便在需要的时候正确地处理窗口的显示和隐藏操作。
  • 树形结构组织方式及其优势
    • WindowState 采用树形结构组织,每个 WindowState 实例都有一个指向父节点的引用,形成了一个层次化的窗口容器树。这种结构就像是一个家族族谱,清晰地展现了窗口之间的层级关系。
    • 这种树形结构的优势在于,它允许 WMS 有效地管理复杂的窗口层级关系。例如,在多窗口场景下,通过这种结构可以很容易地确定窗口的显示顺序,确保正确的绘制顺序和事件分发。当有新窗口添加或者窗口层级发生变化时,WMS 可以通过这个树形结构快速地调整窗口的位置和顺序。
  • 接口实现与功能扩展
    • 它继承自 WindowContainer,实现了 WindowManagerPolicy.WindowState 接口。这种设计将窗口管理的策略和实际实现分离,提高了系统的灵活性和可扩展性。
    • 通过实现这些接口,WindowState 能够更好地与 WMS 的其他组件协作,实现各种复杂的功能。例如,它实现的接口方法可以用于接收和处理来自 WMS 的各种命令,如更新窗口状态、处理动画等,同时也为外部组件提供了统一的访问窗口状态的方式。
  • 输入事件分发与窗口动画协调职责
    • 在输入事件分发方面,WindowState 扮演着重要的角色。它接收并转发输入事件到对应的窗口。当用户在屏幕上进行触摸或者按键操作时,WMS 会根据窗口的层级和位置等信息,将事件发送到对应的 WindowState,然后由它将事件分发给窗口进行处理。
    • 在窗口动画协调方面,它负责处理窗口过渡动画,如淡入淡出、滑动等。例如,当窗口需要显示或者隐藏时,WindowState 会根据预先定义的动画规则,协调窗口的动画效果,使得窗口的显示和隐藏过程更加平滑和自然。

三、WindowToken 类

  • 窗口身份标识的重要性
    • WindowToken 类主要用于标识窗口所属于的应用程序或任务,就像是窗口的 “身份证”。在 Android 系统中,有众多的窗口,为了区分它们所属的应用程序或者任务,就需要一个唯一的标识,这就是 WindowToken 的重要作用。
    • 它确保了窗口身份的唯一性和安全性,防止不同应用程序或者任务的窗口之间出现混淆或者非法访问的情况。例如,在多任务环境下,不同应用程序的窗口可以通过 WindowToken 进行区分,WMS 可以根据 WindowToken 来判断窗口所属的应用程序,从而进行正确的管理和资源分配。
  • 创建时机与关联机制
    • 对于系统窗口(如输入法窗口),通常需要主动创建 WindowToken。这一般是由特定的服务(如 InputMethodManagerService)来完成。在创建窗口时,WMS 会为其生成一个 WindowToken,并将其与窗口相关联。
    • WindowState 通过引用 WindowToken 来识别和管理窗口。这种关联机制就像是把窗口和它的 “身份证” 绑定在一起,使得 WMS 在管理窗口时可以方便地通过 WindowToken 来查找和操作对应的窗口。例如,当需要对某个应用程序的所有窗口进行统一操作(如隐藏或者显示)时,WMS 可以通过 WindowToken 快速定位到这些窗口。

四、Surface 类

  • 图形绘制底层支持功能
    • Surface 类是用于绘制图形内容的底层对象,是窗口显示内容的基础。它就像是一块无形的画布,为窗口提供了绘制图形的场所。每个窗口都有一个对应的 Surface,窗口通过在 Surface 上绘制内容来实现显示。
    • 它提供了一系列的方法和接口,供应用程序进行图形绘制和渲染。例如,应用程序可以使用 Surface 提供的绘图 API 来绘制文本、图像、图形等各种元素,从而构建出丰富多彩的用户界面。
  • 与 WMS 和其他组件的协同工作方式
    • 在 WMS 的工作流程中,Surface 与其他组件密切协作。当 WMS 为窗口创建 SurfaceControl 对象后,SurfaceControl 会管理 Surface 的显示层级和属性,然后将 Surface 提交给 SurfaceFlinger 进行显示。
    • 在窗口的更新过程中,例如当窗口的内容发生变化需要重新绘制时,WMS 会通过 Surface 相关的机制,通知 SurfaceFlinger 重新绘制 Surface,以确保窗口显示的内容能够及时更新。而且,在窗口的生命周期管理中,当窗口销毁时,相关的 Surface 资源也会被释放,避免资源浪费。
作者 east
Flink 1月 8,2025

解决flink Caused by: java.lang.NoClassDefFoundError: org/apache/flink/table/delegation/ExtendedOperationExecutor

运行flink代码报错:

运行报错:Exception in thread "main" org.apache.flink.table.api.TableException: Unexpected error when trying to load service provider.
	at org.apache.flink.table.factories.FactoryUtil.discoverFactories(FactoryUtil.java:826)
	at org.apache.flink.table.factories.FactoryUtil.discoverFactory(FactoryUtil.java:525)
	at org.apache.flink.table.api.internal.TableEnvironmentImpl.create(TableEnvironmentImpl.java:295)
	at org.apache.flink.table.api.internal.TableEnvironmentImpl.create(TableEnvironmentImpl.java:266)
	at org.apache.flink.table.api.TableEnvironment.create(TableEnvironment.java:95)
	at com.xxx.a_jobs.FlinkBatchHiveJob$.main(FlinkBatchHiveJob.scala:35)
	at com.xxx.a_jobs.FlinkBatchHiveJob.main(FlinkBatchHiveJob.scala)
Caused by: java.util.ServiceConfigurationError: org.apache.flink.table.factories.Factory: Provider org.apache.flink.table.planner.delegation.DefaultDialectFactory could not be instantiated
	at java.util.ServiceLoader.fail(ServiceLoader.java:232)
	at java.util.ServiceLoader.access$100(ServiceLoader.java:185)
	at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:384)
	at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
	at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
	at org.apache.flink.table.factories.FactoryUtil.discoverFactories(FactoryUtil.java:817)
	... 6 more
Caused by: java.lang.NoClassDefFoundError: org/apache/flink/table/delegation/ExtendedOperationExecutor
	at java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
	at java.lang.Class.getConstructor0(Class.java:3075)
	at java.lang.Class.newInstance(Class.java:412)
	at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:380)
	... 9 more
Caused by: java.lang.ClassNotFoundException: org.apache.flink.table.delegation.ExtendedOperationExecutor

报错的核心问题是在加载 Flink 的服务提供程序时出现了异常。具体的错误信息是 java.lang.NoClassDefFoundError: org/apache/flink/table/delegation/ExtendedOperationExecutor 和 java.lang.ClassNotFoundException: org.apache.flink.table.delegation.ExtendedOperationExecutor。这通常是由于类缺失或者缺少相应的依赖库导致的。

解析错误信息

  1. 错误描述:Copy CodeCaused by: java.lang.NoClassDefFoundError: org/apache/flink/table/delegation/ExtendedOperationExecutor 这是因为 org.apache.flink.table.delegation.ExtendedOperationExecutor 类未找到。Flink 在尝试加载这个类时,发现它不存在。这通常意味着某个必需的类库没有被正确包含在项目的依赖中。
  2. 原因分析:
    • Flink 依赖的类库中缺少了某些必需的 JAR 文件,或者你的项目中缺少某些必要的依赖。
    • 可能是因为缺少了 Hive 相关的依赖,或者使用的 Flink 版本与 Hive 连接的依赖不兼容。
    • 另外,org.apache.flink.table.planner.delegation.DefaultDialectFactory 类在创建时也无法实例化,这表明整个 Flink SQL 引擎的配置或者依赖加载出现了问题。

解决方案

1. 检查依赖:

确保你的项目中包含了 Flink 和 Hive 的相关依赖,特别是你使用的 Flink 版本与 Hive 相关的 JAR 文件。

对于 Flink 1.17.1 和 Hive,你应该确保以下依赖被正确添加到项目中:

  • Flink和hive 依赖:
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-planner_2.11</artifactId>
    <version>1.17.1</version>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-api-java-bridge</artifactId>
    <version>1.17.1</version>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-hive_2.11</artifactId>
    <version>1.17.1</version>
</dependency>

2. 检查兼容性:

确保你所使用的 Flink 版本与 Hive 连接器的版本兼容。在 Flink 1.17.x 中,某些老旧的 Hive 连接器可能会出现兼容性问题。你可以查阅 Flink 官方文档 查看兼容的版本。

作者 east
Flink 1月 6,2025

解决flink toAppendStream doesn’t support consuming update changes which is produced by node GroupAggregate

下面的flink代码:

String sqlQuery = “SELECT MAX(val) AS max_val, MIN(val) AS min_val FROM dataT GROUP BY pid”;

Table resultTable = tableEnv.sqlQuery(sqlQuery);

DataStream resultStream = tableEnv.toAppendStream(resultTable, Row.class);

运行报错:

org.apache.flink.table.api.TableException: toAppendStream doesn’t support consuming update changes which is produced by node GroupAggregate(groupBy=[pid], select=[pid, MAX(val) AS max_val, MIN(val) AS min_val])

原因分析

报错信息提示 toAppendStream doesn't support consuming update changes which is produced by node GroupAggregate,这意味着在当前代码中使用 toAppendStream 方法去转换结果表为 DataStream 时出现了不兼容的情况。
在 Flink 中,当执行包含聚合操作(比如这里的 GROUP BY 以及 MAX、MIN 聚合函数计算)的 SQL 查询时,查询结果可能会产生更新(update)类型的变更,而 toAppendStream 方法只适用于那种仅追加(append-only)类型的结果,也就是结果表中数据只会新增而不会有更新、删除等变更的情况。这里的聚合操作导致了结果存在更新变化,所以调用 toAppendStream 就抛出了异常,它无法处理这种带有更新的数据变更情况。

正确代码修改思路及示例

要解决这个问题,可以使用 toRetractStream 方法来替代 toAppendStream 方法,toRetractStream 方法可以处理包含更新、删除等多种变更类型的数据,它返回的 DataStream 中元素是包含了一个布尔值标志(表示是新增还是撤回操作)以及实际的数据行(对应查询结果行)的二元组形式。
以下是修改后的代码示例:



        // 2. 添加 Source
        DataStream<RunData> dataSource = env.addSource(new TDengineSourceFunction(jdbcUrl, user, password, query));

        // 3. 注册临时表
        tableEnv.createTemporaryView("rundata", dataSource, "pid, val"); // 根据实际字段调整

        // 4. 执行 SQL 查询以计算最大值和最小值
        String sqlQuery = "SELECT MAX(val) AS max_val, MIN(val) AS min_val FROM dataT GROUP BY pid";
        Table resultTable = tableEnv.sqlQuery(sqlQuery);

        // 5. 将结果转换为 DataStream 并打印,这里使用 toRetractStream 替代 toAppendStream
        DataStream<Tuple2<Boolean, Row>> resultStream = tableEnv.toRetractStream(resultTable, Row.class);
        resultStream.print();

        // 6. 触发执行
        env.execute("Flink SQL Max and Min Calculation");
    }
}
作者 east

上一 1 2 3 下一个

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。回复”chatgpt”获取免注册可用chatgpt。回复“大数据”获取多本大数据电子书

标签

AIGC AI创作 bert chatgpt github GPT-3 gpt3 GTP-3 hive mysql O2O tensorflow UI控件 不含后台 交流 共享经济 出行 图像 地图定位 外卖 多媒体 娱乐 小程序 布局 带后台完整项目 开源项目 搜索 支付 效率 教育 日历 机器学习 深度学习 物流 用户系统 电商 画图 画布(canvas) 社交 签到 联网 读书 资讯 阅读 预订

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 详解Python当中的pip常用命令
  • AUTOSAR如何在多个供应商交付的配置中避免ARXML不兼容?
  • C++thread pool(线程池)设计应关注哪些扩展性问题?
  • 各类MCAL(Microcontroller Abstraction Layer)如何与AUTOSAR工具链解耦?
  • 如何设计AUTOSAR中的“域控制器”以支持未来扩展?
  • C++ 中避免悬挂引用的企业策略有哪些?
  • 嵌入式电机:如何在低速和高负载状态下保持FOC(Field-Oriented Control)算法的电流控制稳定?
  • C++如何在插件式架构中使用反射实现模块隔离?
  • C++如何追踪内存泄漏(valgrind/ASan等)并定位到业务代码?
  • C++大型系统中如何组织头文件和依赖树?

文章归档

  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年1月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年7月
  • 2018年6月

分类目录

  • Android (73)
  • bug清单 (79)
  • C++ (34)
  • Fuchsia (15)
  • php (4)
  • python (43)
  • sklearn (1)
  • 云计算 (20)
  • 人工智能 (61)
    • chatgpt (21)
      • 提示词 (6)
    • Keras (1)
    • Tensorflow (3)
    • 大模型 (1)
    • 智能体 (4)
    • 深度学习 (14)
  • 储能 (44)
  • 前端 (4)
  • 大数据开发 (488)
    • CDH (6)
    • datax (4)
    • doris (30)
    • Elasticsearch (15)
    • Flink (78)
    • flume (7)
    • Hadoop (19)
    • Hbase (23)
    • Hive (40)
    • Impala (2)
    • Java (71)
    • Kafka (10)
    • neo4j (5)
    • shardingsphere (6)
    • solr (5)
    • Spark (99)
    • spring (11)
    • 数据仓库 (9)
    • 数据挖掘 (7)
    • 海豚调度器 (10)
    • 运维 (34)
      • Docker (3)
  • 小游戏代码 (1)
  • 小程序代码 (139)
    • O2O (16)
    • UI控件 (5)
    • 互联网类 (23)
    • 企业类 (6)
    • 地图定位 (9)
    • 多媒体 (6)
    • 工具类 (25)
    • 电商类 (22)
    • 社交 (7)
    • 行业软件 (7)
    • 资讯读书 (11)
  • 嵌入式 (70)
    • autosar (63)
    • RTOS (1)
    • 总线 (1)
  • 开发博客 (16)
    • Harmony (9)
  • 技术架构 (6)
  • 数据库 (32)
    • mongodb (1)
    • mysql (13)
    • pgsql (2)
    • redis (1)
    • tdengine (4)
  • 未分类 (6)
  • 程序员网赚 (20)
    • 广告联盟 (3)
    • 私域流量 (5)
    • 自媒体 (5)
  • 量化投资 (4)
  • 面试 (14)

功能

  • 登录
  • 文章RSS
  • 评论RSS
  • WordPress.org

All Rights Reserved by Gitweixin.本站收集网友上传代码, 如有侵犯版权,请发邮件联系yiyuyos@gmail.com删除.