Android之UiAutomator测试框架源码分析(第十八篇:StaleObjectException异常如何解决?)

(注意:本文基于UI Automator测试框架版本为2.2.0)  

前言

    StaleObjectException是使用UI Automator测试框架时,在个别机型(API版本上)上常见的一个异常(经过证实:其实是所有机型),产生时机:当某个Window中的View树中已经找到的View对象(控件),你正在操作它时,突然从界面变为不可见时,如果你继续使用表示该控件的UiObject2对象,会触发1个StaleObjectException异常对象,此异常表示控件在Window中不可见。

    View中个别控件可能因不可见而被“回收”的特性,官方采用抛出StaleObjectExcetion异常的方式提醒我们(表示控件从可见到不可见),这种情况必须需要处理,不然它会导致测试用例直接失败……我们怎么解决StaleObjectException异常呢?

    2024.03.05,之前说的不严谨,其实在每个机型上都有可能出现该异常,它表示什么呢?请继续往下看,另外抛出一个深度思考的问题,为啥要有StaleObjectException!!

StaleObjectException什么时候出现?

1、使用UiObject2的任意API

2、任何机器、系统

3、某个window中的View树中某个控件被回收了

这3个条件同时发生时,你一定会碰见StaleObjectException

官方建议

    当使用表示控件的UiObject2对象的API时,可能会触发一个StaleObjectException异常,这是因为底层的View已被销毁(多数情况是因为控件不可见),发生这种情况时,你有必要使用UiDevice的findObject(BySelector)方法再次重新获取同一个UiObject2对象(再次获取控件)

    说明:官方明确给出解决方案,捕获到StaleObjectException对象时,可以尝试再次获得一个新的UiObject2对象即可……

StaleObjectException类结构介绍

public class StaleObjectException extends RuntimeException {
}

    StaleObjectException类定义在UIAutomator测试框架中的androidx.test.uiautomator包中,继承RuntimeException,命名符合异常类的要求,望文知义是异常类命名的基本要求,所以类中是什么都没有定义的,只需要使用这个异常对象

StaleObjectException异常在哪被抛出?

    private AccessibilityNodeInfo getAccessibilityNodeInfo() {
             
            …………省略很多代码………………

            if (!mCachedNode.refresh()) {
                throw new StaleObjectException();
            }
             
            …………省略很多哦代码………………
    }

    我在整个UI Automator测试框架中的一番查找……发现UiObject2中的getAccessibilityNodeInfo()方法会抛出StaleObjectException对象,这意味着所有调用getAccessibilityNodeInfo()方法,只要没有捕获该异常对象,就可能抛出这个StaleObjectException对象,继续查找在UI Automator测试框架中的哪些地方调用了UiObject2的getAccessibilityNodeInfo()方法?即可获知所有可能会抛出StaleObjectException对象的位置!

getAccessibilityNodeInfo()方法被调用的位置

    共计25处调用了UiObject2中的getAccessibilityNodeInfo()方法,25处代码中,只有个别的方法显式的捕获了StaleObjectException对象,然后进行了处理,而剩下的API都没有对StaleObjectException作处理。StaleObjectException异常对象会严重影响测试用例程序的稳定性!只要使用UiObject2对象,比如UiObject2常用的findObject()方法、click()方法,setText()方法等等,他们直接或者间接都会调用getAccessibilityNodeInfo()方法,这不是坑爹吗?基本上只要使用UiObject2的API就可能碰见这个异常,尼玛UiObject2也太严谨了吧?哈哈!

    那么如何解决这个StaleObjectException异常?保证程序的绝对稳定性呢??

再次参考官方代码

    public boolean equals(Object object) {
          
         …………省略很多代码…………
         
        try {
            UiObject2 other = (UiObject2)object;
            return getAccessibilityNodeInfo().equals(other.getAccessibilityNodeInfo());
        } catch (StaleObjectException e) {
            return false;
        }
         
    }

    在UiObject2中的equals()方法中,使用try catch语句捕获StaleObjectException对象,然后处理方式特别粗暴,直接return false!我们借鉴一下这个思路,可以在catch代码块中捕获到StaleObjectException异常对象时,再重新通过UiDevice的findObject(BySelector)方法,获取一个新的UiObject2对象!

最终解决方案

    public final boolean handleStale(BySelector bySelector) {
        boolean repeat = true;
        boolean result = false;
        int retryCount = 5;

        while (repeat) {
            try {
                UiObject2 temp = obtainUiObject2(bySelector);
                result = performAction(temp);
                repeat = false;
                if (retryCount <= 0) {
                    break;
                }
                retryCount--;
            } catch (StaleObjectException e) {
                Log.d(TAG, "Handle StaleObjectException");
            }
        }
        return result;
    }

    基本思路:当UiObject2的任意API抛出StaleObjectException异常对象时,直接捕获,接着while循环中一直尝试几次获取一个新的UiObject2对象,直到没有再出现StaleObjectException对象(说明控件可见了),再结束,这样可以完美解决使用UiObject2遇到的StaleObjectException异常

备注:建议增加一个重试的最大次数,还建议增加一个延迟时间,避免过快的重试

总结

    1、StaleObjectException对象的抛出,用于提示我们即将操作的控件突然不可见了,这样可以避免业务逻辑上的误操作,所以UiObject2显然进步了。举个例子:如果使用UiObject,控件不可见的瞬间,仍然会执行点击或者滑动操作,此时的操作则会是同一个坐标内的其它控件……

2、换成UiObject2则就会更严谨,由于控件不可见,则会出现StaleObjectException,尤其是RecycleView、ListView经常会出现StaleObjectException!!!在业务逻辑的选择上,显示UiObject2更佳适用。

3、你可以把StaleObjectException理解成为UiObjectNotFoundException的高级版本,前者更严谨,除了表示找不到控件、也表示控件突然消失不见了。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值