MonkeyRunner MonkeyRunner 实践----用 java 来编写脚本

Anonymous · July 03, 2015 · Last by Anson replied at February 21, 2016 · 1670 hits

前言

之前在论坛看到有同行在用 MonkeyRunner 测试一些 Android 的操作, 使用的是 Python 写的脚本, 就设想应该也是可以用 java 来写脚本的, 毕竟本身 MonkeyRunner 就是用 java 写的, 并调用了一些 Android 平台的 Api. 在网上上搜索了一下 确实也可以,只是网上的例子基本都是直接使用 Android 的 api, 相对来说看起来不是那么友好, 于是本着学习的态度,就自己尝试着学习下 MonkeyRunner 的源代码, 然后试着改下, 再稍微的封装下, 于是便有了这篇帖子. 本帖子中所描述并且给出的代码, 源于 Android 的基础 Api,二次进行的简单修改, 目的在于研究和学习之用. 同时用 java 编写类似 MonkeyRunner 的代码并不需要 MonkeyRunner.jar, 特此声明.

MonkeyRunner 基本结构

  • com.android.monkeyrunner.controller 和 com.android.monkeyrunner.recorder 包下为 MonkeyRunner 的 UI 界面, 标准的 java swing 编写, 界面布局非常简单, 并非像 uiautomatorview 一样使用的 Eclipse RCP.
  • com.android.monkeyrunner 包下才是最主要的东西, 由 Python 文件包含的信息会经由 JythonUtils 进行分析,然后在调用 MonkeyRunner.java 等这些主要的操作类进行操作.
  • 最主要的操作类为 MonkeyRunner, MonkeyDevice, MonkeyView, MonkeyImage, 下文将要进行编写和封装的自定义类,也将和这几个关键类进行匹配.
  • 如果想在 IDE 中编译 MonkeyRunner 的源代码需要 ddmlib.jar,guavalib.jar,sdklib.jar,chimpchat.jar,hierarchyviewer2lib.jar 这五个 jar 包,而应于我们自己编写的也需要着五个包,所以并不需要 MonkeyRunner.jar.

基本思路

简单说就是把 MonkeyRunner 的核心的几个类自己在写一遍, 因为本身并不复杂,所以写起来也用不了多少时间, 而且很多的方法基本都是删减点,然后直接拷贝过去就好了.

扩展对照

MonkeyRunner MteMonkeyRunner Description
MonkeyRunner MteMonkeyRunner 提供一些必要的操作入口,比如关键的连接设备等方法
MonkeyDevice MteMonkeyDevice 提供 Android 设备包括模拟器和真机的基本操作方法
MonkeyView MteMonkeyView 提供标准的界面 view object 的常规操作和封装
MonkeyImage MteMonkeyImage 提供截图的一些基本操作

举个栗子

MonkeyRunner

public static MonkeyDevice waitForConnection(PyObject[] args, String[] kws) {
    ArgParser ap = JythonUtils.createArgParser(args, kws);
    Preconditions.checkNotNull(ap);

    long timeoutMs;
    try {
        double timeoutInSecs = JythonUtils.getFloat(ap, 0);
        timeoutMs = (long) (timeoutInSecs * 1000.0);
    } catch (PyException e) {
        timeoutMs = Long.MAX_VALUE;
    }

    IChimpDevice device = chimpchat.waitForConnection(timeoutMs,
            ap.getString(1, ".*"));
    MonkeyDevice chimpDevice = new MonkeyDevice(device);
    return chimpDevice;
}

MteMonkeyRunner

public static MteMonkeyDevice waitForConnection(long timeoutMs, String deviceId) {
    setChimpChat();
    IChimpDevice device = chimpchat.waitForConnection(timeoutMs, deviceId);

    MteMonkeyDevice mmd = new MteMonkeyDevice(device);

    return mmd;
}
  • setChimpChat()方法为本人自己添加的, 因为如果直接调用 MteMonkeyRunner 或者是 MonkeyRunner 的方法因为 chimpchat 并没有初始化,所以必然抛空指针,所以我自己对 chimpchat 进行了初始化, 请参考下面的代码: java static void setChimpChat() { TreeMap<String, String> options = new TreeMap<String, String>(); options.put("backend", "adb"); chimpchat = ChimpChat.getInstance(options); }

必要的了解

不管是 MonkeyRunner 还是我自己胡写的 MteMonkeyRunner 说穿了主要就是调用下面的几个个关键的 API:

  • ChimpChat
  • IChimpDevice
  • IChimpView
  • IChimpImage 这几个类都来自于 com.android.chimpchat.core package, 也就是 chimpchat.jar. 这个类包中包含了很多基础的关键的 Api, 比如 调用 adb 和 HierarchyViewer 等.

MteMonkeyRunner 基本结构

就像前面提到的我只是把 MonkeyRunner 几个主要的类重写和拷贝了下, 在简单的进行了修改.只有四个主要的类文件.其实就是本人无耻的把对应方法中分析 python 参数的代码都删除掉了. 因为出发点为用 java 直接写代码, 所以并没有重新写个 UI 出来, 但是我分析过 MonkeyRunner 对应的界面代码, 真是挺对付的....

举个栗子

package test.example.testcase;

import java.util.Collection;

import com.mte.android.mmr.MteMonkeyDevice;
import com.mte.android.mmr.MteMonkeyImage;
import com.mte.android.mmr.MteMonkeyRunner;
import com.mte.util.DateTimeUtil;

public class MteMonkeyRunnerTest {

    public static void main(String args[]){

        String apppath="./app/android/oschina/osc-android-app-2.2.apk";
        String packageName="net.oschina.app";
        String startActivity="net.oschina.app/.AppStart";

        MteMonkeyDevice device=MteMonkeyRunner.waitForConnection(100000,"HC477WY00656");

        System.out.println("Device name is : " + device.getProperty("build.model"));

        for(String prop:device.getPropertyList()){

            System.out.println(prop +" : "+device.getProperty(prop));
        }

        device.installPackage(apppath);

        device.startActivity(startActivity);

        MteMonkeyRunner.sleep(30000);

        MteMonkeyImage image=new MteMonkeyImage(device.takeSnapshot());

        image.writeToFile("./screen/MteMonkeyRunnerTest"+DateTimeUtil.getCurrentDateTime()+".png", "png");

        MteMonkeyRunner.sleep(10000);


        Collection<String> viewLst=device.getViewIdList();

        System.out.println("device.getViewIdList() is : "+viewLst.size());

        if(viewLst.size()>=1){
            for(String prop:viewLst){
                System.out.println(prop);
            }
        }

        MteMonkeyRunner.sleep(20000);

        device.removePackage(packageName);

        device.dispose();

    }

}

可能出现的问题以及免责声明

本人在写完基本方法测试脚本的时候, 有时候发现 当我使用 device.shell() 或者是 device.getViewIdList() 等方法时候,会抛出异常, 而且在真机通过了,反而在模拟器上出现问题, 虽然花时间找了下解释, 但是基本上算是没解决, 我在见了自己的 MteMonkeyRunner 的类的时候,只是删除了对应的 python 的参数处理,而且关键的方法基本没变, 怀疑是本人的 Mac 环境问题,所以本人也很无奈, 大家如果有兴趣在使用的时候,请注意并请见谅.写的这些本身就是为了学习研究, 通过一些很具体的实践加深了解, 分析源代码是非常有效的学习方式.

参考

本帖子中的 MteMonkeyRunner 所有源代码都可以在 https://github.com/PandaSense/mtesense 中下载到,包名为 com.mte.android.mmr, 可以单独使用, 只要你把,帖子前面说明的五个 jar 添加到对应 project 的 java build path 就可以.

其他设想

  • 扩展下 MonkeyRunner 的 UI 界面多加一些有用的操作 (自带的界面非常简陋)
  • 接受参数化的脚本,直接跑测试等等
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 10 条回复 时间 点赞

研究的精神点赞~!
不过如果真是要用 Java 去做 Monkeyrunner 的自动化的话,真心觉得是把简单的事情做复杂了...

#1 楼 @anikikun 我的初步想法是能在我的测试 case 里面直接写操作, 但是我查看了下, 基本都是 python 学的例子. 而我用到的 framework 和其他的专业工具都是主要用 java, 更多的时候,都是环境妥协的结果吧.

赞格式,赞帖子。多点实践就更好了。这类帖子我们最欢迎了,加油

才发现网上有 MonkeyRunner 的详细分析的, 中文的. 非常详细,基本上的问题都能解决, 写的很好.
MonkeyRunner 源码剖析

请问下楼主那 5 个 jar 包从哪下载的呢,我这边下载后导入,但是代码里面会报 import com.android.chimpchat.core.IChimpView.AccessibilityIds;这个地方找不到

将原来的方法改成这样就没报错,public int[] getAccessibilityIds()

这个是什么开发工具 ?

#7 楼 @lovoro IDEA,Jetbrain 的, java 的 ide

#5 楼 @miumiu 这几个包 都在 android 的 SDK 下,

楼主, 请教下, 和 Appium 相比, 它的优势和劣势在哪里啊?

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up