Android之UI Automator框架源码分析(第X篇:StaleObjectException异常如何解决?)

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

    StaleObjectException是使用UI Automator测试框架时,在个别机型(API版本上)特别常见的一个异常,当View树中匹配的View对象(控件)被回收时(内存中不存在),如果你继续使用表示该控件的UiObject2对象,就会触发该异常,针对View树可能会被“回收”的系统特性,官方决定采用抛出异常的方式提醒我们,这种情况是需要进行处理的,那么我们怎么解决StaleObjectException异常?

官方解答

    当使用表示控件的UiObject2对象时,可能会抛出一个StaleObjectException异常,这是因为底层View已被销毁,在这种情况下,你很有必要使用UiDevice的findObject(BySelector)方法再次获取一个新的UiObject2实例对象。(说明:官方已经给出指导意见,思路是再次获得一个新的UiObject2对象)

StaleObjectException类简单介绍

public class StaleObjectException extends RuntimeException {
}

StaleObjectException定义在UIAutomator测试框架中的androidx.test.uiautomator包中,只是一个继承RuntimeException的类,也符合异常类最重要的就是它的名字的理念,望文知义是对异常类名字的基本要求,所以类中是什么都没有定义的……

StaleObjectException异常在哪被抛出的?

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

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

经过我的一番查找……发现UiObject2中定义的getAccessibilityNodeInfo()方法,会抛出StaleObjectException对象,这意味着所有调用该方法且没有捕获该异常的方法都可能会抛出这个异常对象,那么又有哪些地方调用了getAccessibilityNodeInfo()方法?

 

getAccessibilityNodeInfo()方法被调用的位置

Android之UI Automator框架源码分析(第X篇:StaleObjectException异常如何解决?)_第1张图片

共计找到25处调用了UiObject2中定义的getAccessibilityNodeInfo()方法,而这25处方法中,只有个别的方法catch捕获StaleObjectException对象,剩下的API,其中有很多是我们经常使用的方法……并没有做任何捕获异常处理,所以这个StaleObjectException异常,只要我们使用UiObject2对象,在有的API版本中是随时可能会触发该异常的(偶现,跟API版本有关),比如UiObject2常用的click方法,setText方法等等,他们直接或者间接都会调用getAccessibilityNodeInfo()方法!

那么如何解决这个异常?保证我们的测试代码极其稳定??

 

先参考官方代码(思路)

    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,这个处理方法对于我们绝对是一个好的思路,我们也可以在捕获到StaleObjectException时,只要重新通过UiDevice的findObject(BySelector)方法,获取一个新的UiObject2对象就是了!

 

最终解决方案

        boolean repeat = true;
        try {
            while (repeat) {
                UiObject2 temp = uidevice.findObject(bySelector);
                if (uiObject2 != null) {
                    uiObject2.click();
                }
                repeat = false;
            }
        } catch (StaleObjectException e) {
            Log.d(TAG, "Handle StaleObjectException");
        }

当UiObject2的click()方法抛出该异常,我们就再次获取一次UiObject2对象,为了规避仍会抛出该异常,所以我采用了while,直到调用UiObject对应的方法不抛出该异常时,才不会再做获取操作,完美的解决了该异常,偶耶!!

 

总结

StaleObjectException对象的抛出,官方是好意,毕竟测试框架只是半成品,不同的系统版本有不同的特性,不过这严重影响测试项目的健壮性,针对这个非必现问题,不处理是不行的,终于被我撸掉你了!

 

你可能感兴趣的:(UI,Automator测试框架)