介绍脱离PC机执行uiautomator2脚本

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

    }
}

相关文章

手机上也可以快速部署大模型,创建本地AI助理,实现无网络AI聊天

前言:本文教你在电脑和手机上安装ollama,通过ollama部署搭建本地大模型,完成本地AI Agent助理的搭建。一、前言介绍7月16日- H2O.AI最近开源了两个可以在智能手机设备上运行的大模...

手机上安装 PDF 处理神器 Stirling PDF

Stirling PDF 是一个开源的 PDF 处理工具,功能很强大。原生有 exe 文件和 docker 支持。不过最近折腾 Termux 于是就想着把 Stirling PDF 也搬到手机上运行。...

把 VS Code 带到安卓 - Code FA

注意,本篇讨论的是不基于pc的 这个是9月份初弄出来的,自己一直在使用,一直没来得及分享,前段时间在b站看到了一个差不多的方案。背景vs code 大部分是由 ts 编写,上层 UI 可以运行在各个系...

基于DeepSeek的本地知识库搭建全流程解析

一、环境准备与模型选择 (一)硬件与系统要求 1.最低配置:16GB内存 + RTX 4060显卡(支持7B参数模型)。 2.推荐配置:32GB内存 + RTX 4090显卡(支持14B模型,处理...

英国开发人员发布开源性实用程序Alfred的代码

据国际文传电讯 8月29日报道,英国开发应用程序Running with Crayons Ltd公司近日表示,该公司的开发人员在 GitHub上发布了Alfred实用程序的代码,旨在可以进行开放源代码...