当前位置: 首页 > news >正文

如何用电脑批量操作多部手机

如果你有很多手机,然后需要在这些手机上同时执行相同的操作,这个时候如果能有一种办法批量操作,将会大大提高效率,节省很多时间。本文将介绍基于uiautomator2实现的群控手机方案。

uiautomator2 是 一种 Android 自动化测试框架,提供了点击、长按、输入文本、滑动、拖拽、截屏等方法,能够模拟用户的各种动作。

本方案采用python的web端框架django+vue前端框架实现,支持在页面选择手机,执行配置的命令,多部手机会同时执行操作(页面做的比较简陋,见谅)。

大致的流程也比较简单,基于在页面配置的命令,调用uiautomator2的相关api实现,有兴趣的朋友也可以了解下uiautomator2相关的接口,动手来实现一遍

这里贴上前端的实现代码

<template><div class="common-layout"><el-container><el-header><div style="font-weight: bolder;text-align: center;font-size: 30px">手机群控</div></el-header><el-main><el-row><el-col :span="12"><el-card class="box-card"><div style="display: flex;justify-content: space-between;align-items:center"><div style="font-weight: bolder;">设备列表</div><el-button link type="text" @click="getDeviceList">刷新</el-button></div><el-table :data="deviceListData" style="width: 100%;" height="75vh" @selection-change="selectDevices" ref="deviceTable"><el-table-column type="selection" width="55"/><el-table-column prop="sn" label="设备编号"/><el-table-column prop="name" label="设备名称"/><el-table-column prop="status" label="状态"/><el-table-column label="操作"><template slot-scope="scope"><el-button link type="text" size="small" @click="editDevice(scope.row)">编辑</el-button><!--<el-button link type="primary" size="small">删除</el-button>--></template></el-table-column></el-table></el-card></el-col><el-col :span="12"><el-card class="box-card"><div style="display: flex;justify-content: space-between;align-items:center"><div style="font-weight: bolder;">命令列表</div><el-button link type="text" @click="addCommandVisible = true">添加</el-button></div><el-table :data="commandListData" style="width: 100%" height="75vh" ><el-table-column width="55"/><el-table-column prop="name" label="名称"/><el-table-column prop="creatTime" label="创建时间"/><el-table-column label="操作"><template slot-scope="scope"><el-button link type="primary" size="small" @click="updateCommand(scope.row)">编辑</el-button><el-button link type="primary" size="small" @click="deleteCommand(scope.row.name)">删除</el-button><el-button link type="primary" size="small" @click="executeCommand(scope.row.name)">执行</el-button></template></el-table-column></el-table></el-card></el-col></el-row></el-main></el-container><el-dialog :visible.sync="addCommandVisible" title="添加/编辑命令"><el-input v-model="commandInfo.name" autocomplete="off" placeholder="请输入命令名称"></el-input><el-table :row-class-name="tableRowClassName" :data="commandInfo.commands" border style="width: 100%"><el-table-column align="center" width="100px" type="index" label="序号"></el-table-column><el-table-column align="center" prop="operation" label="操作"><template slot-scope="scope"><el-select @change="operationChange(scope.row.operation,scope.row.index)" size="small"v-model="scope.row.operation" placeholder="请选择操作类型"><el-option v-for="item in operationOption" :key="item.value" :label="item.label":value="item.value"></el-option></el-select></template></el-table-column><el-table-column align="center" prop="param" label="参数"><template #default="scope"><el-input size="small" v-model="scope.row.param" placeholder="非必填,有则填写"></el-input></template></el-table-column><el-table-column align="center" prop="delayTime" label="延时"><template #default="scope"><el-input size="small" v-model="scope.row.delayTime" placeholder="填写延时时长,单位秒"></el-input></template></el-table-column><el-table-column align="center" prop="date" label="操作"><template slot-scope="scope"><el-button link type="primary" size="small" @click="addForm">添加</el-button><el-button link type="primary" size="small" v-if="commandInfo.commands.length>1"@click="removeIdx(scope.row,scope.row.index)" style="color:rgb(216,30,6)">删除</el-button></template></el-table-column></el-table><template #footer><span class="dialog-footer"><el-button @click="addCommandVisible = false">取消</el-button><el-button type="primary" @click="addCommand">确定</el-button></span></template></el-dialog><el-dialog title="编辑设备" :visible.sync="updateDeviceVisible"><el-form :model="deviceInfo"><el-form-item label="设备编号" :label-width="formLabelWidth"><el-input v-model="deviceInfo.sn" autocomplete="off" disabled></el-input></el-form-item><el-form-item label="设备名称" :label-width="formLabelWidth"><el-input v-model="deviceInfo.name" autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="updateDeviceVisible = false">取 消</el-button><el-button type="primary" @click="updateDevice">确 定</el-button></div></el-dialog></div>
</template>
<script>import {getDeviceList, updateDevice,getCommandList,addCommand,deleteCommand,executeCommand} from '@/api/device'export default {name: 'device',data() {return {formLabelWidth: '140px',addCommandVisible: false,commandInfo: {name:"",commands: [{operation: '',param: '',delayTime: ''}]},operationOption: [{value: '单击',label: '单击'},{value: '返回',label: '返回'},{value: '右滑',label: '右滑'},{value: '截屏',label: '截屏'},{value: '点击Home键',label: '点击Home键'},{value: '卸载',label: '卸载'},{value: '输入文字',label: '输入文字'},{value: '左滑',label: '左滑'}],deviceListData: [],commandListData: [],deviceInfo: {sn: "",name: ""},executeParam:{devices:[],name:""},updateDeviceVisible:false,}},mounted() {this.getDeviceList()this.getCommandList()},methods: {selectDevices(val) {let selection = this.$refs.deviceTable.selection;this.executeParam.devices = []for (let item of selection){this.executeParam.devices.push(item.sn)}console.log(JSON.stringify(this.executeParam.devices))},getDeviceList() {getDeviceList().then(res => {if (res.code === 0) {this.deviceListData = res.data;} else {this.$message.error('查询失败!')}this.loading = false}).catch(err => {this.loading = falsethis.$message.error(err)})},operationChange(operation, index) {this.commandInfo.commands[index].operation = operation},editDevice(data){this.deviceInfo = datathis.updateDeviceVisible = true;},updateDevice() {updateDevice(this.deviceInfo).then(res => {if (res.code === 0) {this.getDeviceList()} else {this.$message.error('查询失败!')}this.loading = falsethis.updateDeviceVisible = false;}).catch(err => {this.loading = falsethis.$message.error(err)})},getCommandList() {getCommandList().then(res => {if (res.code === 0) {this.commandListData = res.data;} else {this.$message.error('查询失败!')}this.loading = false}).catch(err => {this.loading = falsethis.$message.error(err)})},addCommand() {addCommand(this.commandInfo).then(res => {if (res.code === 0) {this.getCommandList()} else {this.$message.error('命令添加失败!')}this.loading = falsethis.addCommandVisible = false;}).catch(err => {this.loading = falsethis.$message.error(err)})},updateCommand(data){this.addCommandVisible = true;this.commandInfo.name = data.namethis.commandInfo.commands = data.commands},deleteCommand(name){deleteCommand(name).then(res => {if (res.code === 0) {this.getCommandList()} else {this.$message.error('查询失败!')}this.loading = false}).catch(err => {this.loading = falsethis.$message.error(err)})},executeCommand(name){if (this.executeParam.devices.length === 0){this.$message.error('请先在左侧选择设备!')return}this.executeParam.name = nameexecuteCommand(this.executeParam).then(res => {if (res.code === 0) {this.$message.success('执行完成!')} else {this.$message.error('执行失败!')}this.loading = false}).catch(err => {this.loading = falsethis.$message.error(err)})},// 添加indextableRowClassName({row, rowIndex}) {row.index = rowIndex},// 添加操作addForm() {if (this.isDataComplete()) {this.commandInfo.commands.push({operation: '',param: '',delayTime: ''})} else {this.$message({message: '请完善信息后再添加',type: 'warning'})}},isDataComplete() {return this.commandInfo.commands.every((item) => item.operation)},// 删除操作removeIdx(item, index) {this.commandInfo.commands.splice(index, 1)this.$message({message: '删除成功',type: 'success'})}}}
</script>
<style scoped></style>

实际的效果可以看下视频演示

如何用电脑批量操作多部手机

相关文章:

如何用电脑批量操作多部手机

如果你有很多手机&#xff0c;然后需要在这些手机上同时执行相同的操作&#xff0c;这个时候如果能有一种办法批量操作&#xff0c;将会大大提高效率&#xff0c;节省很多时间。本文将介绍基于uiautomator2实现的群控手机方案。 uiautomator2 是 一种 Android 自动化测试框架&…...

Delphi 程序例子(DPI变化自动感知及显示器相关功能演示)

目录 一、前言 二、Delphi 演示程序&#xff08;D12版本&#xff0c;用D11也都可以&#xff09; 1. 演示程序功能&#xff1a; 2. 程序界面&#xff1a; 3. 程序源代码下载&#xff08;有偿&#xff09;&#xff1a; 一、前言 系列文章&#xff1a; 彻底搞懂 Windows 显示…...

mysql主从复制的步骤和使用到的操作命令有哪些?

步骤&#xff1a; 配置主服务器&#xff08;Master&#xff09;&#xff1a; 启用二进制日志记录&#xff08;binary logging&#xff09;。配置主服务器的唯一标识&#xff08;server-id&#xff09;。创建用于复制的专用复制账户。 配置从服务器&#xff08;Slave&#xff0…...

[AIGC] Java CompletableFuture:简介及示例

Java 8 引入了一个名为 CompletableFuture 的新库&#xff0c;正如其名称所示&#xff0c;该库提供了一种名为 “Completable Future” 的新 API&#xff0c;其主要目的是支持异步编程&#xff0c;并通过可搜索的操作将这些异步操作进行聚合管控。 文章目录 CompletableFuture …...

五步定位性能瓶颈

一、着手测试前的准备&#xff1a;优化数据流向与系统架构分析 在进行性能测试或系统优化之前&#xff0c;明确数据流向和系统架构的细节是至关重要的步骤。这不仅能够帮助识别潜在的瓶颈&#xff0c;还能确保测试用例设计的全面性与针对性。以下是关键步骤和方法&#xff1a;…...

currentTarget指向监听者Target:指向触发者

在JavaScript的事件处理中&#xff0c;currentTarget 和 target 是两个重要的属性&#xff0c;它们常常用于区分事件处理函数当前绑定的元素和实际触发事件的元素。这两个属性的意义可以用下面的方式解释&#xff1a; currentTarget 指向监听者&#xff1a;这意味着currentTa…...

OpenAI宫斗剧番外篇: “Ilya与Altman联手对抗微软大帝,扫除黑恶势力”,“余华”和“莫言”犀利点评

事情是这样的。 小编我是一个重度的智谱清言用户&#xff0c;最近智谱清言悄悄上线了一个“划词引用”功能后&#xff0c;我仿佛打开了新世界的大门。我甚至用这个小功能&#xff0c;玩出来了即将为你上映的《OpenAI宫斗剧番外篇》。 3.5研究测试&#xff1a;hujiaoai.cn 4研…...

网关路由SpringCloudGateway、nacos配置管理(热更新、动态路由)

文章目录 前言一、网关路由二、SpringCloudGateway1. 路由过滤2. 网关登录校验2.1 鉴权2.2 网关过滤器2.3 登录校验2.3.1 JWT2.3.2 登录校验过滤器 3. 微服务从网关获取用户4. 微服务之间用户信息传递 三、nacos配置管理问题引入3.1 配置共享3.1.1 在Nacos中添加共享配置3.1.2 …...

关于linux的防护,以及群集你要知道的有哪些11-搭建Zabbix监控系统

1、zabbix具备功能 主机的性能监控、网络设备性能监控、数据库性能监控、多种警告方式、详细报表图表绘制 2、zabbix的监测对象 Linux服务器、Windows服务器、路由器、交换机等网络设备 3、zabbix的监控架构 server-client架构&#xff1a;适用于网络比较简单&#xff0c…...

腾讯云环境安装单机版minio

Minio 下载安装 wget https://dl.min.io/server/minio/release/linux-amd64/minio修改minio 文件为可执行文件 chmod x minio3、启动&#xff0c;随机端口启动 ./minio server /data/miniodata # 或者指定密码执行 MINIO_ACCESS_KEYmyminioadmin MINIO_SECRET_KEYmyminioadm…...

蓝桥杯2023(十四届)省赛——统计日期(八重神子)

统计日期 2.日期统计 - 蓝桥云课 (lanqiao.cn) 其实一开始我想直接暴力的&#xff0c;然后写着写着突然觉得可以优化一下&#xff1a; 优化方法&#xff1a;先找所有2023的位置&#xff0c;记录初始和最后的位置 找出所有合法日期的位置&#xff0c;使用前缀和&#xff0c;计…...

【Redis基础知识一】

Redis基础知识One Redis简介为什么用 Redis 作为 MySQL 的缓存&#xff1f;1.Redis 具备高性能2.Redis具备高并发 Redis包含的数据结构Redis里面数据结构的应用场景Redis里面数据结构的实现 Redis线程模型补充什么是跳表跳表特性 Redis采用单线程为什么还这么快 Redis简介 Redi…...

如何在go项目中实现发送邮箱验证码、邮箱+验证码登录

前期准备 GoLand &#xff1a;2024.1.1 下载官网&#xff1a;https://www.jetbrains.com/zh-cn/go/download/other.html Postman&#xff1a; 下载官网&#xff1a;https://www.postman.com/downloads/ 效果图(使用Postman) Google&#xff1a; QQ&#xff1a; And …...

Docker 部署 Nginx 实现一个极简的 负载均衡

背景: Nginx是异步框架的网页服务器&#xff0c;其常用作反向代理(负载均衡器)。在一般的小项目中, 服务器不多, 如果不考虑使用服务注册与发现, 使用Nginx 可以容易实现负载均衡。 在特此写一个快速入门 Nginx 的技术贴, 使用 Docker 部署 Nginx, 实现一个极简的加权轮询负载均…...

Java刷题总结(面试)

1、String类 String不可变 java 中String是 immutable的&#xff0c;也就是不可变&#xff0c;一旦初始化&#xff0c;其引用指向的内容是不可变的。 也就是说&#xff0c;String str “aa”&#xff1b;str“bb”&#xff1b;第二句不是改变“aa”所存储地址的内容&#xf…...

ipad air6电容笔推荐,2024十大高性价比电容笔排行榜!

​电容笔作为ipad的最佳拍档&#xff0c;为学生党和打工人带来了极大的便利&#xff0c;二者搭配效率真的大大提升&#xff0c;但是&#xff0c;如何选购一支适合自己的电容笔呢&#xff1f;作为一个对数码设备非常感兴趣并且有一定了解的人&#xff0c;我根据自己多年的使用经…...

Java Memorandum

Java Memorandum 1 定义安全的集合2 collection集合用迭代器删除元素时避免并发修改异常3 异常捕获4 RequestBody和RequestParam和Parameter区别4.1 RequestBody4.2 RequestParam4.3 Parameter 1 定义安全的集合 void old() {ArrayList<Apple> apples new ArrayList<…...

大数据学习之 Hadoop部署

Hadoop部署 Linux桌面模式关闭 # 设置 systemctl set-default multi-user.target # 重启 reboot防火墙关闭 systemctl status firewalld systemctl stop firewalld # 关闭开机自启 systemctl disable firewalld配置Java环境 echo $JAVA_HOME java -version # Java配置 # 上传ja…...

xxe漏洞--xml外部实体注入漏洞

1.xxe漏洞介绍 XXE&#xff08;XML External Entity Injection&#xff09;是一种攻击技术&#xff0c;它允许攻击者注入恶意的外部实体到XML文档中。如果应用程序处理XML输入时未正确配置&#xff0c;攻击者可以利用这个漏洞访问受影响系统上的敏感文件、执行远程代码、探测内…...

Nginx反向代理与负载均衡:让网站像海豚一样灵活

引言&#xff1a;"当网站遇上海豚&#xff1a;Nginx让数据流动更流畅&#xff01;"想象一下&#xff0c;你的网站是一片繁忙的海域&#xff0c;而Nginx就像一群聪明的海豚&#xff0c;它们不仅能够迅速地找到最佳的捕食路线&#xff08;反向代理&#xff09;&#xf…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...