The Android Developer Challenge is back! Submit your idea before December 2.

monkeyrunner

monkeyrunner 工具提供了一个 API,用于编写可从 Android 代码外部控制 Android 设备或模拟器的程序。使用 monkeyrunner,您可以编写一个 Python 程序,以便安装 Android 应用或测试软件包,运行它,向其发送按键,截取其界面的屏幕截图,并将屏幕截图存储到工作站中。monkeyrunun 工具主要用于在功能/框架级测试应用和设备以及运行单元测试套件,但您也可以随意将其用于其他目的。

monkeyryrunner 工具与 UI/Application Exerciser Monkey(即 monkey 工具)不相关。monkey 工具直接在设备或模拟器上的 adb shell 中运行,并生成伪随机用户和系统事件流。相比之下,monkeyrunner 工具可以从 API 发送特定命令和事件,以便从工作站中控制设备和模拟器。

monkeyrunner 工具为 Android 测试提供了以下独特的功能:

  • 多设备控制:monkeyrunner API 可以跨多个设备或模拟器应用一个或多个测试套件。您可以通过数据线连接所有设备并/或一次性立即启动所有模拟器,以编程方式依次连接到每个设备,然后运行一个或多个测试。您还可以编程方式启动模拟器配置,运行一个或多个测试,然后关闭模拟器。
  • 功能测试:monkeyrunner 可以对 Android 应用运行自动化全面测试。您可以通过按键或轻触事件提供输入值,并查看屏幕截图形式的结果。
  • 回归测试 - monkeyrunner 可通过以下方法测试应用稳定性:运行应用,并将其输出屏幕截图与一组已知正确的屏幕截图进行比较。
  • 可扩展的自动化 - 由于 monkeyrunner 是一个 API 工具包,因此您可以开发一整套系统(包括基于 Python 的模块和用于控制 Android 设备的程序)。除了使用 monkeyrunner API 本身,您还可以使用标准 Python ossubprocess 模块来调用 Android 工具,例如 Android 调试桥

    您还可以将自己的类添加到 monkeyrunner API 中。使用插件扩展 monkeyrunner 部分中对此进行了更详细的介绍。

monkeyrunun 工具使用 Jython,这是一种使用 Java 编程语言的 Python 实现。Jython 使 monkeyrunner API 能与 Android 框架轻松交互。借助 Jython,您可以使用 Python 语法访问 API 的常量、类和方法。

一个简单的 monkeyrunner 程序

下面介绍了一个简单的 monkeyrunner 程序,该程序会连接到一台设备,并创建一个 MonkeyDevice 对象。该程序使用 MonkeyDevice 对象安装 Android 应用软件包,运行其中一个 Activity,并向该 Activity 发送按键事件。然后,该程序会截取结果的屏幕截图,并创建一个 MonkeyImage 对象。在此对象中,该程序会写出包含屏幕截图的 .png 文件。

    # Imports the monkeyrunner modules used by this program
    from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice

    # Connects to the current device, returning a MonkeyDevice object
    device = MonkeyRunner.waitForConnection()

    # Installs the Android package. Notice that this method returns a boolean, so you can test
    # to see if the installation worked.
    device.installPackage('myproject/bin/MyApplication.apk')

    # sets a variable with the package's internal name
    package = 'com.example.android.myapplication'

    # sets a variable with the name of an Activity in the package
    activity = 'com.example.android.myapplication.MainActivity'

    # sets the name of the component to start
    runComponent = package + '/' + activity

    # Runs the component
    device.startActivity(component=runComponent)

    # Presses the Menu button
    device.press('KEYCODE_MENU', MonkeyDevice.DOWN_AND_UP)

    # Takes a screenshot
    result = device.takeSnapshot()

    # Writes the screenshot to a file
    result.writeToFile('myproject/shot1.png','png')
    

monkeyrunner API

monkeyrunner API 包含在 com.android.monkeyrunner 软件包中的三个模块中:

  • MonkeyRunner:一个包含 monkeyrunner 程序的实用方法的类。此类提供了一个将 monkeyrunner 连接到设备或模拟器的方法。它还提供了为 monkeyrunner 程序创建界面以及显示内置帮助的方法。
  • MonkeyDevice:表示设备或模拟器。此类提供了安装和卸载软件包、启动 Activity 以及向应用发送键盘或轻触事件的方法。此外,您还可以使用此类运行测试软件包。
  • MonkeyImage:表示屏幕截图图片。此类提供了截屏、将位图转换为各种格式、比较两个 MonkeyImage 对象以及将图片写入文件的方法。

在 Python 程序中,您可以将每个类作为 Python 模块来访问。monkeyrunner 工具不会自动导入这些模块。要导入模块,请使用 Python from 语句:

    from com.android.monkeyrunner import <module>
    

其中 <module> 是您要导入的类名称。您可以在同一个 from 语句中导入多个模块,只需用英文逗号分隔各模块名称即可。

运行 monkeyrunner

您可以从文件中运行 monkeyrunner 程序,也可以在交互式会话中输入 monkeyrunner 语句。您可以通过调用 monkeyrunner 命令(位于 SDK 目录的 tools/ 子目录中)来执行这两项操作。如果您提供文件名作为参数,monkeyrunner 命令会将文件的内容作为 Python 程序运行;否则,它会启动一个交互式会话。

monkeyrunner 命令的语法为

    monkeyrunner -plugin <plugin_jar> <program_filename> <program_options>
    

表 1 介绍了各个标记和参数。

表 1. monkeyrunner 标记和参数。

参数 说明
-plugin <plugin_jar> (可选)指定包含 monkeyryrunner 插件的 .jar 文件。要详细了解 monkeyrunner 插件,请参阅使用插件扩展 monkeyrunner。要指定多个文件,请多次添加该参数。
<program_filename> 如果您提供此参数,monkeyrunner 命令会将文件的内容作为 Python 程序运行。如果未提供此参数,该命令将启动交互式会话。
<program_options> (可选)该程序在 <program_file> 中的标记和参数。

monkeyrunner 内置帮助

您可以通过运行以下命令为 monkeyrunner 生成 API 参考文档:

    monkeyrunner help.py <format> <outfile>
    

参数如下:

  • <format>text(对于纯文本输出)或 html(对于 HTML 输出)。
  • <outfile> 是输出文件的路径限定名称。

使用插件扩展 monkeyrunner

您可以使用以 Java 编程语言编写的类扩展 monkeyrunner API,并将其编译为一个或多个 .jar 文件。您可以借助此功能使用自己的类扩展 monkeyrunner API,或扩展现有的类。您还可以使用此功能初始化 monongxununner 环境。

要为 monkeyrunner 提供插件,请在调用 monkeyrunner 命令时使用表 1 中所述的 -plugin <plugin_jar> 参数。

在插件代码中,您可以导入和扩展 com.android.monkeyrunner 中的 monkeyrunner 主类 MonkeyDeviceMonkeyImageMonkeyRunner(请参阅 monkeyrunner API)。

请注意,插件不会授予您对 Android SDK 的访问权限。您无法导入 com.android.app 等软件包。这是因为 monkeyrunner 会在框架 API 级别之下与设备或模拟器交互。

插件启动类

插件的 .jar 文件可以指定一个在脚本处理开始之前实例化的类。要指定此类,请将键 MonkeyRunnerStartupRunner 添加到 .jar 文件的清单中。该值应为启动时运行的类的名称。以下代码段展示了如何在 ant 编译脚本中执行此操作:

    <jar jarfile="myplugin" basedir="${build.dir}">
    <manifest>
    <attribute name="MonkeyRunnerStartupRunner" value="com.myapp.myplugin"/>
    </manifest>
    </jar>

    

要访问 monkeyrunner 的运行时环境,启动类可以实现 com.google.common.base.Predicate<PythonInterpreter>。例如,此类会在默认命名空间中设置一些变量:

Kotlin

    package com.android.example

    import com.google.common.base.Predicate
    import org.python.util.PythonInterpreter

    class Main: Predicate<PythonInterpreter> {

        override fun apply(anInterpreter: PythonInterpreter): Boolean {
            /*
             * Examples of creating and initializing variables in the monkeyrunner environment's
             * namespace. During execution, the monkeyrunner program can refer to the variables
             * "newtest" and "use_emulator"
             *
             */
            anInterpreter.set("newtest", "enabled")
            anInterpreter.set("use_emulator", 1)
            return true
        }
    }
    

Java

    package com.android.example;

    import com.google.common.base.Predicate;
    import org.python.util.PythonInterpreter;

    public class Main implements Predicate<PythonInterpreter> {
        @Override
        public boolean apply(PythonInterpreter anInterpreter) {

            /*
            * Examples of creating and initializing variables in the monkeyrunner environment's
            * namespace. During execution, the monkeyrunner program can refer to the variables "newtest"
            * and "use_emulator"
            *
            */
            anInterpreter.set("newtest", "enabled");
            anInterpreter.set("use_emulator", 1);

            return true;
        }
    }