百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

介绍脱离PC机执行uiautomator2脚本

itomcoil 2025-06-12 13:17 13 浏览

前面介绍过了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)

    }
}

相关推荐

selenium(WEB自动化工具)

定义解释Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7,8,9,10,11),MozillaF...

开发利器丨如何使用ELK设计微服务中的日志收集方案?

【摘要】微服务各个组件的相关实践会涉及到工具,本文将会介绍微服务日常开发的一些利器,这些工具帮助我们构建更加健壮的微服务系统,并帮助排查解决微服务系统中的问题与性能瓶颈等。我们将重点介绍微服务架构中...

高并发系统设计:应对每秒数万QPS的架构策略

当面试官问及"如何应对每秒几万QPS(QueriesPerSecond)"时,大概率是想知道你对高并发系统设计的理解有多少。本文将深入探讨从基础设施到应用层面的解决方案。01、理解...

2025 年每个 JavaScript 开发者都应该了解的功能

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.Iteratorhelpers开发者...

JavaScript Array 对象

Array对象Array对象用于在变量中存储多个值:varcars=["Saab","Volvo","BMW"];第一个数组元素的索引值为0,第二个索引值为1,以此类推。更多有...

Gemini 2.5编程全球霸榜,谷歌重回AI王座,神秘模型曝光,奥特曼迎战

刚刚,Gemini2.5Pro编程登顶,6美元性价比碾压Claude3.7Sonnet。不仅如此,谷歌还暗藏着更强的编程模型Dragontail,这次是要彻底翻盘了。谷歌,彻底打了一场漂亮的翻...

动力节点最新JavaScript教程(高级篇),深入学习JavaScript

JavaScript是一种运行在浏览器中的解释型编程语言,它的解释器被称为JavaScript引擎,是浏览器的一部分,JavaScript广泛用于浏览器客户端编程,通常JavaScript脚本是通过嵌...

一文看懂Kiro,其 Spec工作流秒杀Cursor,可移植至Claude Code

当Cursor的“即兴编程”开始拖累项目质量,AWS新晋IDEKiro以Spec工作流打出“先规范后编码”的系统工程思维:需求-设计-任务三件套一次生成,文档与代码同步落地,复杂项目不...

「晚安·好梦」努力只能及格,拼命才能优秀

欢迎光临,浏览之前点击上面的音乐放松一下心情吧!喜欢的话给小编一个关注呀!Effortscanonlypass,anddesperatelycanbeexcellent.努力只能及格...

JavaScript 中 some 与 every 方法的区别是什么?

大家好,很高兴又见面了,我是姜茶的编程笔记,我们一起学习前端相关领域技术,共同进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力在JavaScript中,Array.protot...

10个高效的Python爬虫框架,你用过几个?

小型爬虫需求,requests库+bs4库就能解决;大型爬虫数据,尤其涉及异步抓取、内容管理及后续扩展等功能时,就需要用到爬虫框架了。下面介绍了10个爬虫框架,大家可以学习使用!1.Scrapysc...

12个高效的Python爬虫框架,你用过几个?

实现爬虫技术的编程环境有很多种,Java、Python、C++等都可以用来爬虫。但很多人选择Python来写爬虫,为什么呢?因为Python确实很适合做爬虫,丰富的第三方库十分强大,简单几行代码便可实...

pip3 install pyspider报错问题解决

运行如下命令报错:>>>pip3installpyspider观察上面的报错问题,需要安装pycurl。是到这个网址:http://www.lfd.uci.edu/~gohlke...

PySpider框架的使用

PysiderPysider是一个国人用Python编写的、带有强大的WebUI的网络爬虫系统,它支持多种数据库、任务监控、项目管理、结果查看、URL去重等强大的功能。安装pip3inst...

「机器学习」神经网络的激活函数、并通过python实现激活函数

神经网络的激活函数、并通过python实现whatis激活函数感知机的网络结构如下:左图中,偏置b没有被画出来,如果要表示出b,可以像右图那样做。用数学式来表示感知机:上面这个数学式子可以被改写:...