Android自动化学习笔记:编写MonkeyRunner脚本的几种方式

----------------------------------------------------------------------------------------------------------------------------

小记: 之前用的是公司自己研发的自动化工具,对市面开源的自动化工具知之甚少,所以开始自学开源的自动化工具。

初步学习中,难免会有疏漏和想不到的地方,随着不断深入的了解,如有新的体会,会及时修改,不断进步。

2014-10-17:初版

2014-10-20: 更新通过ID写脚本的实例

2014-10-23:更新HierarchyViewer提示和获取ID的方法

2014-10-24:更新第三方插件内容

2014-10-27:更新Q&A--解决了困扰两天的问题

2016-2-24:删除一种方式

------------------------------------------------------------------------------------------------------------------------------

提示:

如果HierarchyViewer无法在市场上的android手机上使用,使用的条件是手机必须是工程机:

1.手机已经Root

2.设备支持启动view service

二者缺一不可。

可以看下Android的官方文档中提到这么一句:
To preserve security, Hierarchy Viewer can only connect to devices running a developer version of the Android system.

翻译过来意思就是说:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器

网上有一些如何把手机root和打开view service的文章,建议不要尝试,手机容易变砖或者不断重启

如果实在按耐不住想拿自己手机试试,可以访问这里:如何在Root的手机上开启ViewServer,使得HierachyViewer能够连接

================================================================================

研究monkeyrunner已经有5天了,前面更新了几篇笔记,先总结一下感觉:

  • MonkeyRunner可以通过获取坐标点去点击目标对象,在引入EasyMonkeyDevice后可以根据ID进行点击对象
  • MonkeyRunner也有第三方的插件,增加了一些实用方法,不过没有花时间尝试
  • Eclipse上写Python代码时,很多对象没有成员函数提示(即使导入过jar包也无用,不过如果写过同样的代码,放在同包的其他文件里,会有部分成员函数的提示信息) :如MonkeyRunner.waitForConnection()获得device对象后,后面devie.不能自动提示可用成员函数
  • 性能不是很好,在使用hierarchyviewer获取ID时,明显感觉需要很长时间
  • 稳定性也不是很好,在使用hierarchyviewer获取ID时,有时候成功,有时候会失败
  • 没有任何junit的继承,尝试使用unit框架来使用MonkeyRunner,不过遇到importError。
  • 官网API更新不及时,资料很少,网上找来找去,内容基本都相差无几
  • 不过相较robotium支持多设备连接,这个确实是一大优点

今天内容是整理一下最新学到的monkeyrunner脚本的几种方式。

1. 通过光标定位,采用上下左右导航键控制(记忆中是Android刚出来的时候,寝室室友买了个HTC的机子有导航键,现在的机子基本都是直板,所以……)

上:device.press(“KEYCODE_DPAD_UP”,”DOWN_AND_UP”)
下:device.press(“KEYCODE_DPAD_DOWN”,”DOWN_AND_UP”)
左:device.press(“KEYCODE_DPAD_LEFT”,”DOWN_AND_UP”)
右:device.press(“KEYCODE_DPAD_RIGHT”,”DOWN_AND_UP”)

2. 通过指定坐标的方式(这种方式是最直接的,也是最笨的,因为不同设备的分辨率不同,脚本不可复用,不过可以直接计算出新的坐标,以后再说……)

详细测试脚本可参照Android自动化学习笔记:MonkeyRunner官方介绍和简单实例 实例是用HierarchyViewer得到坐标,然后touch坐标或者press key event写的monkeyrunner脚本。 
触摸:touch (integer x, integer y, integer type) Sends a touch event specified by type to the screen location specified by x and y.

点击:press (string name, dictionary type) Sends the key event specified by type to the key specified by keycode.

 

3. 通过录制回放的方式(这种方式能够迅速生成一个脚本,不过不是python的脚本)

详细测试脚本可参照:Android自动化学习笔记:MonkeyRunner录制和回放

 

4. 通过获取控件文本的方式(用于apk里控件文本不相同的时候使用,所以如果文本显示相同的话,不可使用,这需要采用第三方插件viewClient)

详细配置和使用方法可参考: https://github.com/dtmilano/AndroidViewClient 自己去看咯.

已经独立成单独一个工具,可以忽略。

 

5. 通过获取控件ID的方式(这种方式比较合适,写一次脚本适用于不同分辨率的手机,但是也需要APK的ID不重复)

下面对calculator.apk来写monkeyrunner脚本:

得到ID: 打开HierarchyViewer->点击Load view Hierarchy

A. 第一种写法:获取HierarchyViewer对象

这里我们也有两个方法:
①可以引入 from com.android.chimpchat.hierarchyviewer import HierarchyViewer

②也可以直接通过hierarchyviewer = device.getHierarchyViewer()来创建

monkeydevice里的源码:

示例:

获取控件viewNode->得到中心点坐标->选择

'''
Created on Oct 20, 2014

@author: deldong
'''
import sys

from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice,MonkeyImage

logFile = open("C:\\monkeyrunner_log.txt","a+")

#连接设备
device = MonkeyRunner.waitForConnection()
if not device:
    print("Please connect a device to start!")
    logFile.write("Please connect a device to start!\n")
else:
    print("Device Connected successfully!")
    logFile.write("Device Connected successfully!\n")
    
package = 'com.android.calculator2' 
activity = 'com.android.calculator2.Calculator'
runComponent = package + '/' + activity

#启动计算器
print("Start calculator")
logFile.write("Start calculator")
device.startActivity(component=runComponent)

#等待时间
print("Wait 15s")
logFile.write("Wait 15s")
MonkeyRunner.sleep(15)

#获取控件对象
print("Get widget object")
logFile.write("Get widget object")
hierarchyviewer = device.getHierarchyViewer()
#得到8,9,*,=的viewnode对象
#举例:viewNode对象的值:com.android.calculator2.CalculatorDisplay@b3ffd170 
viewDigit8 = hierarchyviewer.findViewById("id/digit8")
viewDigit9 = hierarchyviewer.findViewById("id/digit9")
viewMul = hierarchyviewer.findViewById("id/mul")
viewEqual = hierarchyviewer.findViewById("id/equal")

#获取控件的中心坐标
#举例:pointView的中心点坐标的值:Point {240, 116}
print("Get location of view")
logFile.write("Get location of view")
pointViewDigit8 = hierarchyviewer.getAbsoluteCenterOfView(viewDigit8)
pointViewDigit9 = hierarchyviewer.getAbsoluteCenterOfView(viewDigit9)
pointViewMul = hierarchyviewer.getAbsoluteCenterOfView(viewMul)
pointViewEqual = hierarchyviewer.getAbsoluteCenterOfView(viewEqual)

#计算8*9=72的结果
print("touch 8*9=")
logFile.write("touch 8*9=")
MonkeyRunner.sleep(5)
device.touch(pointViewDigit8,'DOWN_AND_UP')
device.touch(pointViewMul,'DOWN_AND_UP')
device.touch(pointViewDigit9,'DOWN_AND_UP')
device.touch(pointViewEqual,'DOWN_AND_UP')

logFile.close()    

 B. 第二种写法:通过EasyMonkeyDevice类和By类来调用控件ID

'''
Created on Oct 23, 2014

@author: deldong
'''
import sys
import math
reload(sys)
sys.setdefaultencoding("utf-8")

from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice
from com.android.monkeyrunner.easy import EasyMonkeyDevice
from com.android.monkeyrunner.easy import By

#connect device
print('connect device!')
device = MonkeyRunner.waitForConnection()

#start activity
print('start activity')
package = 'com.android.calculator2' 
activity = 'com.android.calculator2.Calculator'
runComponent = package + '/' + activity
device.startActivity(component=runComponent)
MonkeyRunner.sleep(15)

#init easymonkeydevice object ,this is init method
print('init easymonkeydevice')
easy_device = EasyMonkeyDevice(device)  

print('Tap 8')
easy_device.touch(By.id('id/digit8'),MonkeyDevice.DOWN_AND_UP)
MonkeyRunner.sleep(1.0)
print('Tap *')
easy_device.touch(By.id('id/mul'),MonkeyDevice.DOWN_AND_UP)
MonkeyRunner.sleep(1.0)
print('Tap 9')
easy_device.touch(By.id('id/digit9'),MonkeyDevice.DOWN_AND_UP)
MonkeyRunner.sleep(1.0)
print('Tap =')
easy_device.touch(By.id('id/equal'),MonkeyDevice.DOWN_AND_UP)

MonkeyRunner.sleep(1.0)
pic = device.takeSnapshot()
pic.writeToFile('./result.png','png')
print('test finished!')    

 C. 第三种写法:通过wrapeasymonkey来写

 先上网址(打不开的用代理) :

https://sourceforge.net/projects/wrapeasymonkey/

http://blog.whoistester.com

中文博客:http://lihao.cf/category/my-work/wrapeasydevice/

在官网上复制的内容:

本文章内容在仅windows 7上经过试验,使用该库主要目的,
1. 将monkeyDevice 与 easyMonkeyDevice统一封装到库
2. 进行自动化操作时, 增加对异常处理,防止异常退出 (例如 当某textview未显示出来时, 捕捉对该textview的操作异常,防止测试脚本异常退出. 如果手动在脚本里加入sleep语句, 但sleep的时间难以指定)
3. 增加了一些比较便捷的函数, 降低了使用 monkeyDevice 和 easyMonkeyDevice 的复杂度。
另需要说明的是: 该wrapEasyMonkey 库是加入了androidviewclient库。 androidviewclient库是Diego Torres Milano 开发的。

如何使用呢。 请先到sourceforge进行下载wrapEasyMonkey源码。(可能需要挂代理)

 

Q&A:

问题1:执行时,出现importError : no module named xxx的异常:

解决方法:

这个问题比较麻烦,因为直接用python + 文件方式,就可以正常执行。

但是用monkeyrunner + 文件的方式,就会有importError产生。

捣鼓了几天,一直没搞清楚到底是哪里的问题。不过今天很开心的是,尝试出一个临时解决这个问题的方式。

写了一个简单的脚本说明:

package1包里有个testPy文件:

import sys
print(sys.path)

from package2 import helperPy

helperPy.printHelper()

if __name__ == '__main__':
    pass

package2包里有个helperPy文件,里面有个printHelper方法

'''
Created on Oct 27, 2014

@author: deldong
'''

def printHelper():
    print("this is print helper")

if __name__ == '__main__':
    pass

执行:

可以看出,环境变量里,只有package1的文件路径,没有package2的。

那怎么办呢,解决方式就是,咱们自己把package2给加进去。

import sys
sys.path.append("C:\\Nokia\\adt-bundle-windows-x86-20140702\\eclipse\\workspace\\项目名")

解决问题灵感来自:

1. Monkeyrunner ImportError: No module named util

2. python怎么import指定文件夹下的模块

3. Python中导入子文件夹中的模块

问题2: wrapEasyMonkey.py脚本报错,提示:'ImportError: cannot import name ViewNode'

解决方法:将wrapEasyMonkey.py脚本中的from com.android.hierarchyviewerlib.device import ViewNode修改为from com.android.hierarchyviewerlib.models import ViewNode

 

posted @ 2014-10-23 17:08  疲惫的豆豆  阅读(8897)  评论(2编辑  收藏  举报