当前位置:首页 > 嵌入式 > 嵌入式软件
[导读]今年在HTML5开发中使用AngularJS框架,觉得MVVM的框架太方便了,双向绑定机制省去了很多繁琐的工作,于是想在新的Android项目也引入类似的MVVM框架,提高团队的开发效率和代

使用Android自动化的方式,不仅可以用来对Android APP进行自动化测试,同样可以用来进行一些其他非常有意思的自动化任务.常用的自动化工具有Monkeyrunner, Robotium, Appium等.Monkeyrunner是Android自带的自动化测试工具,允许用户对Android设备的UI界面进行元素提取,执行touch和drag等操作,配合HierarchyViewer等模块可以非常方便地进行自动化操作.

首先,用户需要安装好Android开发环境,同时运行Monkeyrunner脚本需要安装Jython环境.Jython允许使用Python的语法格式来编写自动化测试代码,因此对于Python开发者而言非常有优势.Python中的一些个别模块不能直接用于Jython中,这时就需要安装适用于Jython版本的,具体方法可参考

http://stackoverflow.com/questions/3256135/importing-python-modules-in-jython. 如安装bottle模块, jython ez_setup.py bottle,然后在使用时导入该模块即可.

?

1
2
3
import sys
sys.path.append('/home/jython2.5.3/Lib/site-packages/bottle-0.12.7-py2.5.egg')
from bottle import Bottle, run, request, response, get, post

使用Monkeyrunner进行Android自动化大概可以分为以下几种类型的操作:设备及UI界面操作,UI界面元素提取,截图对比等.

1, 设备及UI界面操作

其实,涉及到Android设备的操作,使用开发环境自带的adb已经足够了,而Monkeyrunner也是将adb操作封装了以下而已.常见adb操作如下:

?

1
2
3
4
5
6
7
8
adb install xxx.apk 安装apk文件
adb shell am start -an com.xxx.xxx/.MainActivity 启动APP
adb shell am force-stop com.xxx.xxx 停止该APP
adb shell input keyevent KEYCODE_HOME 模拟Android的HOME按键
adb -s emulator-5554 shell input text test_to_input 针对特定的一个模拟器进行操作
adb shell input tap x y 模拟屏幕touch操作
adb shell input swipe x1 y1 x2 y2 模拟屏幕滑动操作
adb devices 查看所有在线的Android设备.

详细的adb命令,可以通过adb -h来查询.而Monkeyrunner中对设备的操作如下:

?

1
2
3
4
5
6
7
8
9
10
11
12
from com.android.monkeyrunner import MonkeyRunner,MonkeyDevice
device = MonkeyRunner.waitForConnection(5,"emulator-5554")
device.shell("am start -an com.xxx.xxx/.MainActivity")
device.touch(250, 450, 'DOWN_AND_UP')
device.drag((1080/2, 1700),(1080/2, 400),0.5,1)
device.type("text to type")
device.shell("input text" + "text to input")
device.press("KEYCODE_HOME")
# 另外,也可以通过id来进行touch操作,此时可以引入By模块,可以非常方便通过id寻找对应的元素.
from com.android.monkeyrunner.easy import EasyMonkeyDevice, By
easy_device = EasyMonkeyDevice(device)
easy_device.touch(By.id('id/button1'), easy_device.DOWN_AND_UP)

以上方式其实与adb shell的操作是一致的,只是方便用户在Jython脚本文件中调用而已.

2, UI界面元素提取

Monkeyrunner可以通过HierarchyViewer来对UI界面的元素进行解析,解析的结果与DDMS及Android Studio中的Android Device Monitor保持一致.

首先需要先对UI界面进行解析,然后即可通过元素id和其他的属性来提取该元素,并对其所有属性进行解析.

?

1
2
3
4
5
6
7
8
9
10
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
from com.android.chimpchat.hierarchyviewer import HierarchyViewer
device = MonkeyRunner.waitForConnection(5,"emulator-5554")
hViewer = device.getHierarchyViewer() # 对当前UI视图进行解析
content = hViewer.findViewById('id/content')  # 通过id查找对应元素
memberView = content.children[0]
text = memberView.namedProperties.get('text:mText').value.encode('utf8')
desc = memberView.namedProperties.get('accessibility:getContentDescription()').value.encode('utf8')
mleft = memberView.namedProperties.get('layout:mLeft').value.encode('utf8')
height = memberView.height

使用HierarchyViewer来解析界面的层级关系,并根据id来查找特定元素是我们常用的做法.然而,Android APP中,会有很多元素是没有对应的id的(这一点,可以通过DDMS或者AVD中解析结果看出来),那么此时,我们如果要精准地找到一个特定元素,就只能通过进一步解析某个元素的children来实现,会比较麻烦,但往往是非常精准的.

需要注意的是,使用HierarchyViewer并通过id来查找元素偶尔会出错,提示找不到对应的元素.如果遇到实在难以解析出来的元素,可以考虑使用另一个模块AndroidViewClient进行解析.原理也很类似.甚至有时候,写法比HierarchyViewer简洁得多.

?

1
2
3
4
5
6
7
vc = ViewClient(device=device, serialno="emulator-5554")
content = vc.findViewById('id/content')
memberView = content.children[0]
text = memberView.getText()
x = memberView.getX()
y = memberView.getY()
height = memberView.getHeight()

AndroidViewClient的项目地址是https://github.com/dtmilano/AndroidViewClient.使用时候有个注意事项,我们先将AndroidViewClient写入环境变量中,然后要先导入AndroidViewClient的模块,之后再导入Monkeyrunner及相应地其他模块,否则会出现找不到AndroidViewClient的错误.至于为什么,大家可以自己尝试一下就明白了.

?

1
2
3
4
5
6
7
8
9
10
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
ANDROID_VIEW_CLIENT_HOME = os.environ['ANDROID_VIEW_CLIENT_HOME']
sys.path.append(ANDROID_VIEW_CLIENT_HOME + '/src')
from com.dtmilano.android.viewclient import ViewClient, View
 
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
from com.android.monkeyrunner.easy import EasyMonkeyDevice, By
from com.android.chimpchat.hierarchyviewer import HierarchyViewer

不过以上两种方式,都有可能出现UI元素解析失败的情况,原因可能是Android相应工具自身的不完善导致的.因为DDMS和AVD也会经常出现无法解析某个UI界面的情况.

具体的应用场景大家自己斟酌吧,总之,能够完美获取到所需元素即可.

3, 截图对比

这是Monkeyrunner非常有特色的一种方式,常用于通过设备屏幕前后的对比来获取对执行结果的判断.

?

1
2
3
4
5
6
7
8
9
10
11
path = "/tmp/images"
image = device.takeSnapshot() # 截图
image.writeToFile(path+"主页面".decode('utf-8')+now+'.png','png')
#下面就开始对之前的截图进行对比了
#去文件中找到我们要对比的图片,与该截图image1进行对比
result = MonkeyRunner.loadImageFromFile('/tmp/images/result.png')
#判断图片相识度是否是为90%
if(image.sameAs(result,0.9)):
    log.write("图片对比成功……\n")
else:
    log.write("主页面图片对比失败……\n")

以上,就是通过Monkeyrunner进行Android自动化的一些基本内容.

下边,将大家容易遇到的一些坑记录下来,造福广大人民群众.

Monkeyrunner容易遇到的一些坑:

1, 中文输入的问题

Monkeyrunner默认只支持Ascii编码,所以遇到中文,目前是不能通过adb的input和type进行输入的.那么可以采用复制到PC剪贴板,然后到Android模拟器里边进行粘贴的方式.

但需要注意的是,Android模拟器里边的剪贴板的内容是当前PC的焦点从PC桌面环境切换到模拟器界面瞬间时的剪贴板内容.常见情况是,通过Monkeyrunner脚本文件将PC环境中剪贴板内容向Android模拟器粘贴时,往往会出现粘贴不上我们想要的内容.此时,出于调试目的,我们会检查在当前PC环境的剪贴板中,是否是我们需要的内容.然后将鼠标焦点移入模拟器中,常常发现能够粘贴上所需的正确内容.,然而,这其实是一个时间差的原因,即PC中的剪贴板内容正确,然后切换到模拟器界面,剪贴板内容是从PC环境带过来的,当然是正确的了.相反,我们在Monkeyrunner脚本执行后,在剪贴板操作之前即将当前PC的焦点切换到模拟器中,会发现剪贴板内容是不正确的.说得有点乱,大家可以好好琢磨,自己实践一下.

github上有位同学写了一个小的工具,可以非常方便地执行Android模拟器中的剪贴板操作,https://github.com/bingwei/inputchineseviaadb.非常好用,大家可以试一试.当然,遇到一些特殊字符,还是需要做一些简单地转义等操作的.

2, Monkeyrunner脚本中各个操作的耗时问题

在Monkeyrunner脚本执行过程中,使用HierarchyViewer以及AndroidViewClient进行界面元素解析时,会发现findViewById操作的时间消耗很大.尤其是该UI界面上元素非常多的时候,耗时非常明显.然而,涉及到界面切换时,我们常常会根据当前界面中是否包含某个id的元素来判断界面是否切换成功.那么,在大多数情况下,我们没有必要根据id来判断当前界面,通过windowName = device.getHierarchyViewer().focusedWindowName()这种方式,已经足够我们进行绝大多数的UI界面判断了,并且在效率上绝对不是一个数量级的提升.

3, 涉及到UI界面之间切换的算法问题

我们常常会遇到,明明就在几个特定的UI界面之间相互切换,但由于Android自动化环境及工具自身的不稳定性,经常导致屏幕切换延迟,点击或切换不成功,APP卡住甚至闪退等一些非常苦恼的问题.那么这就是考验编码能力和算法功底的时候.在这一点上,非常感谢教授同学的帮助,赞一个.

我们可以预先定义一个所有常见界面的focusdWindowName及屏幕切换所需的操作行为的结构体,如

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SCREEN_SWITCH_ACTION = {
    'Activity1': {
        'Activity1': None,
        'Activity2': ('LEFT_DOWN', 'Activity1'), # 如,从Activity2切换到Activity1需要做LEFT_DOWN的操作.
        'Activity3': ('LEFT_DOWN', 'Activity1'),
    },
    'Activity2': {
        'Activity1': ('MID_DOWN', 'Activity2'),
        'Activity2': None,
        'Activity3': ('MID_DOWN', 'Activity2'),
    },
    'Activity3': {
        'Activity1': ('RIGHT_DOWN', 'Activity3'),
        'Activity2': ('RIGHT_DOWN', 'Activity3'),          
        'Activity3': None,
    },
}

如上,该字典中key是目标屏幕,其value值即代表了从当前屏幕切换到目标屏幕所需的操作行为.其中,LEFT_DOWN等可以是简单地touch一个button,也可以写出一个负责的根据界面及元素来决定操作行为的负责操作的集合.有了如上的这种结构体,我们只需要写一个对应的算法,在屏幕切换时从该结构体中解析操作并执行即可.诸如屏幕等待,失败重试之类的容错机制,都可以随意添加了.

其实,涉及到这个算法层面的问题,研究和改进的空间实在是太大了.有兴趣的同学可以更深入的讨论,欢迎指教.

当然,除了Monkeyrunner, Robotium和Appium等工具也都是使用率非常高的,各有优劣吧.

 

以上这些,就是本次Monkeyrunner自动化的一些总结,写的比较简略,欢迎批评指正.

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭