问题

最近升级 appium 的 driver 从 uiautomator1.0 升到 uiautomator2.0,之前的 xpath 中部分使用不了
案例:

(//*[@text='' and @content-desc='start_pager' and @resource-id='com.suning.snmessenger:id/loading_item_view'])[1]

错误日志

05-03 17:04:29.346 18582-19939/io.appium.uiautomator2.server E/appium: Element not found: 
io.appium.uiautomator2.common.exceptions.ElementNotFoundException: Could not find an element using supplied strategy. 
    at io.appium.uiautomator2.handler.FindElement.getXPathUiObject(FindElement.java:79)
    at io.appium.uiautomator2.handler.FindElement.findElement(FindElement.java:161)
    at io.appium.uiautomator2.handler.FindElement.safeHandle(FindElement.java:110)
    at io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:54)
    at io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:204)
    at io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:195)
    at io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:44)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:435)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
    at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:250)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1294)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:911)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:611)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:514)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:468)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:438)
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
    at java.lang.Thread.run(Thread.java:818)

github 链接

https://github.com/appium/appium/issues/8329
提交了 issue,跟 appium 的同学请教后,原因是@text=''导致的

我的解决方案

修改 uiautomator server 源码

public AppiumResponse safeHandle(IHttpRequest request) {
    try {
        Logger.info("Find element command");
        KnownElements ke = new KnownElements();
        final JSONObject payload = getPayload(request);
        final String method = payload.getString("strategy");
        String selector = payload.getString("selector");
        if (selector.contains("@text=''")) {
            selector = selector.replace("@text=''", "not(boolean(string(@text)))");
        }
        if (selector.contains("@content-desc=''")) {
            selector = selector.replace("@content-desc=''", "not(boolean(string(@content-desc)))");
        }
        if (selector.contains("@resource-id=''")) {
            selector = selector.replace("@resource-id=''", "not(boolean(string(@resource-id)))");
        }
        final String contextId = payload.getString("context");
        Logger.info(String.format("find element command using '%s' with selector '%s'.", method, selector));
        final By by = new NativeAndroidBySelector().pickFrom(method, selector);

这样原来 ui1.0 设计的许多脚本就不用逐一修改了。

引玉

切到 ui2.0 后,如何点击弹窗的?分两个场景:

  1. ui2.0 的 server 还没有起来--本人想法,这时仍然可以使用 ui1.0 来点击弹窗,在起 ui2.0 的 server 前,将 ui1.0 停止运行
  2. ui2.0 的 server 已经起来了--ui2.0 本身也是支持弹窗验证的吧


↙↙↙阅读原文可查看相关链接,并与作者交流