介绍脱离PC机执行uiautomator2脚本
itomcoil 2025-06-12 13:17 7 浏览
前面介绍过了python uiautomator2的大概情况,今天主要介绍一下怎么在脱离PC机的情况下执行uiautomator2的脚本。
现在说python uiautomator2的脚本怎么脱机执行。
要在Android上配置python运行环境,有以下几个可以选择:
1. Qpython
2. pydroid3
3. Termux
根据其他网友的踩坑经验,推荐使用Termux来配置环境,步骤如下:
1. 下载Termux,通过adb安装到手机上;
2. 启动Termux,在Termux里执行更新资源的命令:
pkg update
pkg updrade
3. 安装python:
pkg install python(安装完成后可以执行python,检查是否成功)
4. 安装运行UIAutomator2需要的库:
apt install libxml libxslt
apt install libjpeg-turbo
5. 安装UIAutomator2库:
pip install –upgrade –pre uiautomator2
虽然上面已经配置了很多东西,但是实际情况是即使已经完成了上面的配置,还不能马上执行脚本。
6. 需要在PC上初始化手机上的atx-agent,就是在连接了手机时,执行命令:
python –m uiautomator2 init
7. 由于步骤6默认安装的atx-agent位置是/data/local/tmp/atx-agent,而Termux访问不了这个文件,两种解决方案:
7.1 每次启动手机时都在PC端启动它,命令:
adb shell /data/local/tmp/atx-agent server –d #启动并后台运行
7.2 把atx-agent移动到Termux能访问的位置,比如$HOME下面,赋予权限755
上面两种方案的区别是通过adb启动,atx-agent获得的是root权限,可以唤起app而无需鉴权,如果在Termux启动,相当于在一个子系统启动,权限等同于Termux,不能操作其他app。
8. 完成以上的准备,就可以在Android执行python脚本了,假设脚本名称是test_script.py,且已经存储到了Android系统中,可以通过执行命令启动:
python test_script.py
最后提醒一点,在pc上执行和在Android上执行,ut.connect()的参数不一样,在Android上需要这样写:driver = ut.connect(“127.0.0.1:7912”)
看到这里是不是不想玩python uiautomator2脱机执行了?
明明我只是想刷一下小游戏的分数,或者想在年会上抢个小奖品,至于搞得这么麻烦吗?当然不至于,因为还有另一个方式,直接在Android项目中写uiautomator2的脚本。
下面就来介绍写在Android项目中的uiautomator2脚本脱机执行方式,当然这种实现方式也有个前提条件,给这个Android工程root权限或者一些特定的权限,最好是手机root。
来说一下实现思路:
第一步:新建一个Android工程
第二步:在布局文件上加一个Button控件,并注册监听事件
第三步:实现第二步中的监听事件具体逻辑,逻辑包括生成执行脚本的命令,执行命令
第四步:新建一个module,在里面写具体的执行脚本
下面分享一下代码,我这便是以kotlin展示。
这个是Android工程的activity文件,主要是实现android页面上那个执行按钮的逻辑:
package com.example.mytest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.Toast
import com.example.mytest.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnRun.setOnClickListener{runMyUiautomator()}
}
private fun runMyUiautomator() {
Log.i(TAG, "runMyUiautomator: ")
UiautomatorThread().start()
Toast.makeText(this, "start run", Toast.LENGTH_SHORT).show()
}
class UiautomatorThread : Thread() {
override fun run() {
super.run()
val commond = generateCommand("com.example.mytestcase", "ExampleInstrumentedTest", "test1")
val cmdUtils = CMDUtils()
val res: CMDUtils.CMDResult = cmdUtils.execCommand(commond, false)
Log.i("UiautomatorThread", "run: " + res?.error + "--------" + res?.success)
}
fun generateCommand(pkgName: String, clsName: String, mtdNam: String): String {
val commond = "am instrument -w -m -e debug false -e class \'$pkgName.$clsName#$mtdNam\' $pkgName.test/androidx.test.runner.AndroidJUnitRunner"
Log.i("generateCommand: ", commond)
return commond
}
}
}
这个是执行脚本的工具类:
package com.example.mytest
import android.util.Log
import java.io.*
class CMDUtils {
private val TAG = "CMDUtil"
class CMDResult(var resultCode: Int = -1, var error: String = "error", var success: String = "success")
val tag = "CommandExecution"
val COMMAND_SU = "su"
val COMMAND_SH = "sh"
val COMMAND_EXIT = "exit\n"
val COMMAND_LINE_END = "\n"
fun execCommand(command: String, isRoot: Boolean): CMDResult {
val commandResult = CMDResult()
if (command.isEmpty()) {
return commandResult
}
var process: Process? = null
var os: DataOutputStream? = null
var successResult: BufferedReader? = null
var errorResult: BufferedReader? = null
var successMsg: StringBuilder? = null
var errorMsg: StringBuilder? = null
try {
process = Runtime.getRuntime().exec(if (isRoot) COMMAND_SU else COMMAND_SH)
os = DataOutputStream(process.outputStream)
os.write(command.toByteArray())
os.writeBytes(COMMAND_LINE_END)
os.flush()
os.writeBytes(COMMAND_EXIT)
os.flush()
commandResult.resultCode = process.waitFor()
// 获取错误信息
successMsg = StringBuilder()
errorMsg = StringBuilder()
successResult = BufferedReader(InputStreamReader(process.inputStream))
errorResult = BufferedReader(InputStreamReader(process.errorStream))
var s: String? = successResult.readLine()
while (s != null) {
successMsg.append(s)
s = successResult.readLine()
}
s = errorResult.readLine()
while (s != null) {
errorMsg.append(s)
s = errorResult.readLine()
}
commandResult.success = successMsg.toString()
commandResult.error = errorMsg.toString()
Log.i(tag, commandResult.resultCode.toString() + " | " + commandResult.success + " | " + commandResult.error)
} catch (e: IOException) {
val errMsg = e.message
when {
errMsg != null -> Log.e(tag, errMsg)
else -> e.printStackTrace()
}
} catch (e: Exception) {
val errMsg = e.message
when {
errMsg != null -> Log.e(tag, errMsg)
else -> e.printStackTrace()
}
} finally {
try {
os?.close()
successResult?.close()
errorResult?.close()
} catch (e: IOException) {
val errMsg = e.message
when {
errMsg != null -> Log.e(tag, errMsg)
else -> e.printStackTrace()
}
}
process?.destroy()
}
return commandResult
}
}
这个是真正的uiautomator2脚本,当然这里我是随便写的,脚本的意思是启动微信,点击一个空间,然后进行滑动:
package com.example.mytestcase
import android.content.Context
import android.content.Intent
import androidx.test.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleInstrumentedTest {
private lateinit var mDevice: UiDevice
@Test
fun test1() {
// Context of the app under test.
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val packageName = "com.tencent.mm"
val activityName = "com.tencent.mm.ui.LauncherUI"
val context: Context = InstrumentationRegistry.getContext()
val intent: Intent? = context.packageManager.getLaunchIntentForPackage(packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
context.startActivity(intent)
mDevice.findObject(UiSelector().resourceId("com.tencent.mm:id/a4k")).click()
mDevice.swipe(50, 500, 50, 50, 1)
}
}
相关推荐
- CentOS7服务器,这样搭建Tensorflow很快!我可以提前去吃饭了
-
CentOS7搭建Tensorflow框架凡是我相信的,我都做了;凡是我做了的事,都是全身心地投入去做的。WhateverIbelieved,Idid;andwhateverIdid,...
- python2.0和python3.0的区别(python2.7和3.7哪个好)
-
Python3.0是Python语言的一次重大升级,与Python2.x系列存在许多不兼容的改动。以下是两者核心区别的全面总结,按重要性和使用频率排序:一、最关键的破坏性变更特性Pyth...
- 体验无GIL的自由线程Python:Python 3.13 新特征之一
-
全局解释器锁(GIL,GlobalInterpreterLock)是Python中备受争议的特性之一。它的主要作用是确保Python是一种线程安全的编程语言,防止多个线程同时访问和修改同一...
- Python 3.8异步并发编程指南(python异步调用)
-
有效的提高程序执行效率的两种方法是异步和并发,Golang,node.js之所以可以有很高执行效率主要是他们的协程和异步并发机制。实际上异步和并发是每一种现代语言都在追求的特性,当然Python也不例...
- Python测试框架pytest入门基础(pytest框架搭建)
-
Pytest简介Pytestisamaturefull-featuredPythontestingtoolthathelpsyouwritebetterprograms.T...
- Python学不会来打我(8)字符串string类型深度解析
-
2025年全球开发者调查显示,90%的Python项目涉及字符串处理,而高效使用字符串可提升代码效率40%。本文系统拆解字符串核心操作,涵盖文本处理、数据清洗、模板生成等八大场景,助你掌握字符串编程精...
- windows使用pyenv安装多python版本环境
-
官方的介绍。pyenvletsyoueasilyswitchbetweenmultipleversionsofPython.It’ssimple,unobtrusive,an...
- Python 中 base64 编码与解码(Python 中 base64 编码与解码生成)
-
base64是经常使用的一种加密方式,在Python中有专门的库支持。本文主要介绍在Python2和Python3中的使用区别:在Python2环境:Python2.7.16(d...
- Python项目整洁的秘诀:深入理解__init__.py文件
-
当你发现项目中import语句越来越混乱时,问题可能出在缺少这个关键文件上作为一名Python开发者,我曾深陷项目结构混乱的困境。直到真正理解了__init__.py文件的价值,我的代码世界才变得井然...
- 如何把一个Python应用程序装进Docker
-
准备容器无处不在,但是如何在Docker容器中运行Python应用程序呢?这篇文章将告诉你怎么做!如果您想知道,这些示例需要Python3.x。在深入讨论容器之前,让我们进一步讨论一下我们想要封装的...
- python中数值比较大小的8种经典比较方法,不允许你还不知道
-
在Python中比较数值大小是基础但重要的操作。以下是8种经典比较方法及其应用场景,从基础到进阶的完整指南:1.基础比较运算符Python提供6种基础比较运算符:a,b=5,3...
- Python程序员必看3分钟掌握if语句10个神技,第5个99%的人不知道
-
同事因为写错一个if被开除?全网疯传的Python避坑指南,看完我连夜改了代码!一、新手必踩的3大天坑(附救命代码)技巧1:缩进踩坑事件ifTrue:print("这样写必报错!...
- 为什么Python里遍历字符串比列表慢?3个底层原因揭秘
-
用字符串处理文本时,你可能正悄悄浪费性能。在日常Python开发中,我们经常需要遍历字符串和列表。但你是否注意过,当处理海量数据时,遍历字符串的速度明显比列表慢?这背后隐藏着Python设计的深层逻辑...
- 记录Python3.7.4更新到Python.3.7.8
-
Python官网Python安装包下载下载文件名称运行后选择升级选项等待安装安装完毕打开IDLE使用Python...
- Python3中最常用的5种线程锁你会用吗
-
前言本章节将继续围绕threading模块讲解,基本上是纯理论偏多。对于日常开发者来讲很少会使用到本章节的内容,但是对框架作者等是必备知识,同时也是高频的面试常见问题。私信小编01即可获取大量Pyth...
- 一周热门
- 最近发表
-
- CentOS7服务器,这样搭建Tensorflow很快!我可以提前去吃饭了
- python2.0和python3.0的区别(python2.7和3.7哪个好)
- 体验无GIL的自由线程Python:Python 3.13 新特征之一
- Python 3.8异步并发编程指南(python异步调用)
- Python测试框架pytest入门基础(pytest框架搭建)
- Python学不会来打我(8)字符串string类型深度解析
- windows使用pyenv安装多python版本环境
- Python 中 base64 编码与解码(Python 中 base64 编码与解码生成)
- Python项目整洁的秘诀:深入理解__init__.py文件
- 如何把一个Python应用程序装进Docker
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)