就是 udid,亲测可行,
如果是通过 wifiadb 连接的话 udid 就是类似于这种 172.18.101.150:5555
如果是直接 usb 连接的话 就是 760BBKU229HU
反正通过 adb devices 查看就可以了。如果不行,建议贴出 appium server 的 log
你的 log 貌似截的不对哦,看 log 还是在 swipe,接着截屏都是没问题的,应该再截下面一些的 log。
#3 楼 @chenhengjie123 是不是说我二次开发的 apk 只要丢到 appium 的那个 setting 的路径下就可以了?
把里面 self.去掉应该就没什么了,建议去看下 python 的基础。
#1 楼 @chenhengjie123 那回到开始的问题,appium 有办法能使 listview 滚到底部吗?
#2 楼 @cpfeng0124 为了验证刚才你说的 我重新试了下,如果服务端在远端,脚本里面配置远端的 IP 地址类似于 172.18.48.123
那么 如果 Server Address 如果是 127.0.0.1 是不行的,只有改成本机的 IP 才可以。你在再验证实施。
但是你输入 127.0.0.1 跟输入本机的 ip 地址是有区别的,类似于你的服务器放在云端,你的脚本放在本地,这个时候这个 serverAddress 就有用处了啊。
#7 楼 @jennyhui 我想说实际上加分号是可以的 只是 appium 做了些处理。
我查找的代码如下
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("'+Id+'");new UiSelector().clickable(false)')
接着看看 appium for windows 的 log
实际上两条 selector 语句都是会执行的,但是问题来了为什么 log 打印还是采用
Using: UiSelector[RESOURCE_ID=com.seewo.teachercare:id/pass_notice_list_add_notice_imageButton]
这个呢,而忽略了我的后面那个 clickable 的 selector 实际上看 FIND 函数就知道了
final List<UiSelector> selectors = getSelectors(strategy, text, multiple);
if (!multiple) {
for (int i = 0; i < selectors.size() && !found; i++) {
try {
Logger.debug("Using: " + selectors.get(i).toString());
result = fetchElement(selectors.get(i), contextId);
found = result != null;
} catch (final ElementNotFoundException ignored) {
}
}
}
这里实际上查找按顺序来,如果找到的话 就赋值给了 found,接着 for 循环中如果 found 为空的情况下才会继续往下走,所以前面只是查找到 resoureID 就跳出来了,我估计分号的用法是个或的关系 即如果这种情况下找不到 再换另外一种 selector 进行查找为了验证这个 我换下我的查找
driver.find_element_by_android_uiautomator('new UiSelector().className("hehe.hehe");new UiSelector().resourceId("'+Id+'")')
这个时候看下 log 就一目了然了。
结论:分号的用法是一个或的用法,前面成立的话后面就不执行了。
#10 楼 @chenhengjie123
好处是不仅能支持 Android(不过有点问题)是什么问题?
感觉楼主说的貌似没错,看代码那么写,如果有第二个 scrollview 那就滑动不了了?
#5 楼 @jennyhui 有点明白了,但是我更疑惑的点是在于
还是同一个段代码
private void consumeStatement() throws UiSelectorSyntaxException {
String statement;
int index = 0;
int parenCount = -1; // semicolons could appear inside String arguments, so we make sure we only count occurrences outside of a parenthesis pair
while (index < text.length()) {
if (text.charAt(index) == ';' && parenCount == 0) {
break;
}
if (text.charAt(index) == '(') {
if (parenCount < 0) {
parenCount = 1;
} else {
parenCount++;
}
}
if (text.charAt(index) == ')') {
parenCount--;
}
index++;
}
statement = text.substring(0, index);
if (UiScrollableParser.isUiScrollable(statement)) {
Logger.debug("Parsing scrollable: " + statement);
selectors.add(scrollableParser.parse(statement));
} else {
Logger.debug("Parsing selector: " + statement);
selectors.add(selectorParser.parse(statement));
}
text = text.substring(index);
}
因为 selectors 是一个 List所以如果说存在有分号的话,那就应该会产生多个 selector 就是根据分号来进行划分的吧。
难道说还可以有这种用法?
driver.findElement(MobileBy.AndroidUIAutomator("new UiSelector().index(0);new UiSelector().checked(true)"));
#2 楼 @jennyhui
重新又看了下源码,调试一步步走 大概知道了一些,这里只是说 Bootstrap 是如何执行到 UiSelectorParser
首先看 appium windows 的 log
可以知道的一点是 appium 发送给移动端的消息是
["find",{"strategy":"-android uiautomator","selector":"new UiSelector().resourceId(\"com.seewo.teachercare:id/pass_vote_body\")","context":"","multiple":false}]
看 doctorq 关于 Bootstrap 的源码分析就可以的出来 这里的 ACTION 是 find,
那就简单了 进到 Find.java 中看看就知道了。
private AndroidCommandResult execute(final AndroidCommand command,
final boolean isRetry) throws JSONException {
final Hashtable<String, Object> params = command.params();
// only makes sense on a device
final Strategy strategy;
try {
strategy = Strategy.fromString((String) params.get("strategy"));
} catch (final InvalidStrategyException e) {
return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND, e.getMessage());
}
final String contextId = (String) params.get("context");
final String text = (String) params.get("selector");
final boolean multiple = (Boolean) params.get("multiple");
Logger.debug("Finding " + text + " using " + strategy.toString()
+ " with the contextId: " + contextId + " multiple: " + multiple);
boolean found = false;
try {
Object result = null;
final List<UiSelector> selectors = getSelectors(strategy, text, multiple);
我这里只是截取了部分,看到最后一句 getSelector 返回的是一个 List所以肯定是在 getSelectors 中进行查找的
在看看 getSelectors
private List<UiSelector> getSelectors(final Strategy strategy,
final String text, final boolean many) throws InvalidStrategyException,
ElementNotFoundException, UiSelectorSyntaxException,
ParserConfigurationException, InvalidSelectorException {
final List<UiSelector> selectors = new ArrayList<UiSelector>();
UiSelector sel = new UiSelector();
switch (strategy) {
case XPATH:
for (final UiSelector selector : getXPathSelectors(text, many)) {
selectors.add(selector);
}
break;
case CLASS_NAME:
sel = sel.className(text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
break;
case ID:
// There are three types of ids on Android.
// 1. resourceId (API >= 18)
// 2. accessibility id (content description)
// 3. strings.xml id
//
// If text is a resource id then only use the resource id selector.
if (API_18) {
if (resourceIdRegex.matcher(text).matches()) {
sel = sel.resourceId(text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
break;
} else {
// not a fully qualified resource id
// transform "textToBeChanged" into:
// com.example.android.testing.espresso.BasicSample:id/textToBeChanged
// android:id/textToBeChanged
// either it's prefixed with the app package or the android system page.
String pkg = (String) params.get("pkg");
if (pkg != null) {
sel = sel.resourceId(pkg + ":id/" + text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
}
sel = sel.resourceId("android:id/" + text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
}
}
// must create a new selector or the selector from
// the resourceId search will cause problems
sel = new UiSelector().description(text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
// resource id and content description failed to match
// so the strings.xml selector is used
final UiSelector stringsXmlSelector = stringsXmlId(many, text);
if (stringsXmlSelector != null) {
selectors.add(stringsXmlSelector);
}
break;
case ACCESSIBILITY_ID:
sel = sel.description(text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
break;
case NAME:
sel = new UiSelector().description(text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
sel = new UiSelector().text(text);
if (!many) {
sel = sel.instance(0);
}
selectors.add(sel);
break;
case ANDROID_UIAUTOMATOR:
List<UiSelector> parsedSelectors;
try {
parsedSelectors = uiAutomatorParser.parse(text);
} catch (final UiSelectorSyntaxException e) {
throw new UiSelectorSyntaxException(
"Could not parse UiSelector argument: " + e.getMessage());
}
for (final UiSelector selector : parsedSelectors) {
selectors.add(selector);
}
break;
case LINK_TEXT:
case PARTIAL_LINK_TEXT:
case CSS_SELECTOR:
default:
throw new InvalidStrategyException("Sorry, we don't support the '"
+ strategy.getStrategyName() + "' locator strategy yet");
}
return selectors;
}
内容很多,但是我们只看我们需要的 也就是 Strategy 为 ANDROID_UIAUTOMATOR 的
看到这里 就知道了 parsedSelectors = uiAutomatorParser.parse(text);
调用了 uiAutomatorParser 的解析,剩下的就简单了,uiAutomatorParser 的工作先对传进来的参数做些处理 类似于去掉空格之类的再来就判断你的字符串是 new UiSelecotor 还是 new UiScrollable 分别到对应的地方进行解析
不过这里还有个疑问,
while (index < text.length()) {
if (text.charAt(index) == ';' && parenCount == 0) {
break;
}
if (text.charAt(index) == '(') {
if (parenCount < 0) {
parenCount = 1;
} else {
parenCount++;
}
}
if (text.charAt(index) == ')') {
parenCount--;
}
index++;
}
uiAutomatorParser 会判断文本内容中是否包含有';'这个又是干什么呢,
#63 楼 @adfghzhang 其实我想知道你是怎么解决的。。
#1 楼 @chenhengjie123 是的 目前我做的就是每一个点击的操作做 writeFile,我觉得我写的还是比较麻烦。
#59 楼 @adfghzhang https://plus.google.com/108487870030743970488/posts/2TrMqs1ZGQv 这个链接网上说可以解决问题,但是我修改失败了 所以我直接修改 DebugBridge 中
f (toolsDir == null) {
toolsDir ="D:\\AndroidSDK\\android-sdk-windows\\android-sdk-windows\\tools";
// return null;;
}
就可以了