京东质量社区 UIAutomatorview 简单封装 与 Appium Boostrap 结合

taki for 京东 · April 01, 2016 · Last by replied at June 21, 2017 · 2899 hits
本帖已被设为精华帖!

前几天稍微改装了一下 UIAutoatorview 生成脚本,刚发完帖子发现论坛已经有人发出来了,闭门造车不是个好习惯哈,我做这个东西的初衷呢?就是是这段时间学习APPIUM觉得写脚本调式费劲,就是想能确定元素是否真的可以定位到,可以执行,最终的一切都是为了支持Appium的执行,目前程序完成的比较粗糙,健壮性也不太好,主要是为了给不是特别深入的朋友提供些思路和参考


我这里面的检查执行呢?底层调用时通过Appium的Bootstrap实现,这样才能保持和Appium环境一致,如果用坐标点确定的话,生成的脚本放到Appium里面也不一定是有效的,执行环境变了


具体实现,大家看过Bootstrap的会知道,Bootstrap其实是发布了一个Socket服务端,并且是长连接,所以当Bootstrap运行起来我们可以通过Socket去连接发送请求

/**
* @Descrition:执行初始化连接
* */

public void init() {
try {
String host = "127.0.0.1";
int port = 4724;
InetSocketAddress remote = new InetSocketAddress(host, port);
sc = SocketChannel.open();
sc.connect(remote);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Descrition:发送请求
* */

public void sendMsg(){
boolean run = true;
while (run) {


String sendMsg = QueueMsg.getSendMsg(); //这里简单弄了个队列信息
if (null!=sendMsg&&!sendMsg.equals("")) {
printMsg(sendMsg, sendMsg);
InputStream inputStream = new ByteArrayInputStream(sendMsg.getBytes());
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str = br.readLine();
printMsg(sendMsg, "读入一行数据,开始发送...");
w_bBuf = ByteBuffer.wrap(str.getBytes("UTF-8"));
//将缓冲区中数据写入通道
sc.write(w_bBuf);
printMsg(sendMsg, "数据发送成功...");
w_bBuf.clear();
printMsg(sendMsg, "接收服务器端响应消息...");
try {
Thread.currentThread();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
r_bBuf.clear();
//将字节序列从此通道中读入给定的缓冲区r_bBuf
sc.read(r_bBuf);
r_bBuf.flip();
String resultMsg = Charset.forName("UTF-8").decode(r_bBuf).toString();
QueueMsg.setResultMsg(resultMsg);
}
}
}

我们看UIAutomatorView这个工具去Dump控件文件会发现它是执行的UIAutoamtor,Boostrap也是执行的UIAutoamtor,那么我们启动Boostrap之后,线程是阻塞的,所以在用UIAutomatorView去Dump是无法Dump的,所以我们需要对Boostrap进行改造一下,通过Boostrap去Dump控件文件

首先在Boostrap中的AndroidCommandExecutor.java类要加入一行命令

map.put("dump", new Dump());

然后写一个Dump类

public class Dump extends CommandHandler {
private static final File dumpFolder = new File(Environment.getDataDirectory(), "local/tmp");
private static final String dumpFileName = "myuidump.xml";
@Override
public AndroidCommandResult execute(AndroidCommand command) {
File file = new File(dumpFolder, dumpFileName);
try {
QueryController queryController = UiAutomatorBridge.getInstance().getQueryController();
AccessibilityNodeInfo accessibilityRootNode = queryController.getAccessibilityRootNode();
String msg = "success";
try {
AccessibilityNodeInfoDumper.dumpWindowToFile(accessibilityRootNode, file);
} catch (Exception e) {
msg = e.toString();
}
return getSuccessResult(msg);
} catch (final Exception e) {
Logger.debug("Dump Exception: " + e);
return getErrorResult(e.getMessage());
}
}
}

完成之后重新打Bootstrap放到设备里

UIAutomator里面的UiAutomatorHelper类的getUiHierarchyFile方法要改动一下,通过Bootstrap去Dump控件文件

monitor.subTask("Taking UI XML snapshot...");
//原来的Dunp方法

//try {
// device.executeShellCommand(
// command,
// new CollectingOutputReceiver(commandCompleteLatch),
// XML_CAPTURE_TIMEOUT_SEC * 1000);
// commandCompleteLatch.await(XML_CAPTURE_TIMEOUT_SEC, TimeUnit.SECONDS);
//
// monitor.subTask("Pull UI XML snapshot from device...");
// device.getSyncService().pullFile(UIDUMP_DEVICE_PATH,
// dst.getAbsolutePath(), SyncService.getNullProgressMonitor());
// } catch (Exception e) {
// throw new RuntimeException(e);
// }


//修改后的Dunp方法
try {
QueueMsg.setSendMsg("{\"cmd\":\"action\",\"action\":\"dump\",\"params\":{}} ");
monitor.subTask("Pull UI XML snapshot from device...");
String value = QueueMsg.getResultMsg();
BootstrapCmdDomain bootstrapCmdDomain = JSON.parseObject(value, BootstrapCmdDomain.class);
int status = bootstrapCmdDomain.getStatus();
if (0 == status) {
device.getSyncService().pullFile(MyUIDUMP_DEVICE_PATH,
dst.getAbsolutePath(), SyncService.getNullProgressMonitor());
} else {
throw new Exception(bootstrapCmdDomain.getValue());
}
} catch (Exception e) {
throw new RuntimeException(e);
}

{\"cmd\":\"action\",\"action\":\"dump\",\"params\":{}} 这写个命令可以在Appium的日志里获取,大家看我的命令多打了一个空格,因为Bootstrap读取消息的时候少读了一个字节,会报出转JSON异常,所以这里面一定要多加一个空格。

剩下就是封装一些点击事件,输入文本脚本生成的事情了


完成后的结果,我们可以手动把APP启动起来,然后打开UIAtuomator,弄一个输入的事件


选择一个元素,鼠标右键


确定后设备里面的输入被执行了,右侧的文本框打印出了脚本,这里的打印信息,只有事件被正确执行才能生成脚本,生成的脚本规则是自己定义的,所以这里按照自己的需求写自己的算法



点击事件,我们点击下【搜索按钮】

点击【搜索按钮】后的结果


UI界面什么的都比较粗糙,其实就把它做一个辅助工具,为Appium服务,所有功能OK就可以了,当然大家可以自己加一些拖拽、JS执行、长按等事件的操作

共收到 16 条回复 时间 点赞
taki #1 · April 01, 2016 作者

dump 用 UiDevice.getInstance().dumpWindowHierarchy(); 自这个也可以

先谢谢大神,说好的工程文件呢?麻烦给个链接

taki #3 · April 01, 2016 作者

#2楼 @lanxiangtechnical 核心类已经写上去了,你参考自己弄弄才能有进步

#3楼 @taki 还是失败了 私聊下我吧

请指点一下bootstrap项目如何调试,打包后的bootstrap放到设置的什么位置?

taki #6 · April 13, 2016 作者

#5楼 @adfghzhang 我没咋调试,打完放在 /data/local/tmp 下面

@taki QueueMsg是您自定义的类?另外init()和sendMsg()方法是用来测试bootstrap如何工作的么?能联系一下我邮箱私聊问点问题么?

taki #8 · April 14, 2016 作者

#7楼 @adfghzhang 自己定义的

#8楼 @taki 方便公布一下该类的代码么

taki #10 · April 14, 2016 作者

#9楼 @adfghzhang public class QueueMsg {
public static List sendList = new ArrayList();
public static List resultList = new ArrayList();

public static void setSendMsg(String sendMsg) {
sendList.add(sendMsg);
}
public synchronized static String getResultMsg() {
String resultMsg = null;
for (int i = 0; i < 20; i++) {
if (resultList.size() > 0) {
resultMsg = new String(resultList.get(0));
resultList.remove(0);
break;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException ii) {
ii.printStackTrace();
}
}
}
return resultMsg;
}
public static void setResultMsg(String resultMsg) {
resultList.add(resultMsg);
}
public synchronized static String getSendMsg() {
String sendMsg = "";
if (sendList.size() > 0) {
sendMsg = sendList.get(0);
sendList.remove(0);
sendFlag = 0;
}
return sendMsg ;
}
}

你好,请问下具体实现方式是否就是直接发送指令给bootstrap监听的4724端口执行,但是这样做是不是就是绕开了appium的服务端?比如:Webelement e = driver.findElementById(xxx),e.click(); 你需要自己手动把他解析成bootstrap能执行的指令,那这个怎么实现呢?

taki #12 · June 06, 2016 作者

#11楼 @alwans 抓取appium 日志 你观察一下

#12楼 @taki 感谢,我看到了。。。

#12楼 @taki 为什么不能直接把数据给appium的服务端呢?这样省去了自己解析成bootstrap执行的指令

taki #15 · June 06, 2016 作者

#14楼 @alwans 那不是多绕了一层 使用多麻烦

taki [Topic was deleted] 中提及了此贴 15 Jul 16:45

没看明白这样封装的目的是啥?楼主能否解释下?才学自动化不久

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