package io.appium.android.bootstrap.handler; import android.os.SystemClock; import com.android.uiautomator.common.ReflectionUtils; import com.android.uiautomator.core.UiObjectNotFoundException; import io.appium.android.bootstrap.Logger; import java.lang.reflect.Method; /** * This handler is used to long click elements in the Android UI. * */ public class TouchLongClick extends TouchEvent { /* * UiAutomator has a broken longClick, so we'll try to implement it using the * touchDown / touchUp events. */ private boolean correctLongClick(final int x, final int y, final int duration) { try { /* * bridge.getClass() returns ShellUiAutomatorBridge on API 18/19 so use * the super class. */ final ReflectionUtils utils = new ReflectionUtils(); final Method touchDown = utils.getControllerMethod("touchDown", int.class, int.class); final Method touchUp = utils.getControllerMethod("touchUp", int.class, int.class); if ((Boolean) touchDown.invoke(utils.getController(), x, y)) { SystemClock.sleep(duration); if ((Boolean) touchUp.invoke(utils.getController(), x, y)) { return true; } } return false; } catch (final Exception e) { Logger.debug("Problem invoking correct long click: " + e); return false; } } @Override protected boolean executeTouchEvent() throws UiObjectNotFoundException { final Object paramDuration = params.get("duration"); int duration = 2000; // two seconds if (paramDuration != null) { duration = Integer.parseInt(paramDuration.toString()); } printEventDebugLine("TouchLongClick", duration); if (correctLongClick(clickX, clickY, duration)) { return true; } // if correctLongClick failed and we have an element // then uiautomator's longClick is used as a fallback. if (isElement) { Logger.debug("Falling back to broken longClick"); return el.longClick(); } return false; } }
TouchLongClick类继承于TouchEvent,而TouchEvent继承于CommandHandler.调用TouchEvent的execute的方法中,调用了executeTouchEvent方法,所以我们来看上面的executeTouchEvent就好了,执行长点击事件,在uiautomator里有UiObject.longClick()方法,但是写过case的人知道,有时候这个方法达不到我们的需求,但是我们是自己了反射调用TouchDown和TouchUp两个个方法,而在appium里帮你解决了,它自己就帮你做到了这一点,如果你传入到是控件对象,那无可厚非,还是调用UiObject.longClick方法,如果你想根据坐标,时间在点击的话,那么就调用currectLongClick这个appium给你封装好的方法。
final ReflectionUtils utils = new ReflectionUtils(); final Method touchDown = utils.getControllerMethod("touchDown", int.class, int.class); final Method touchUp = utils.getControllerMethod("touchUp", int.class, int.class);
通过反射得到uiautomator里的没有公开的类,从而我们想要的方法touchDown和touchUp.
public ReflectionUtils() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException { final UiDevice device = UiDevice.getInstance(); final Object bridge = enableField(device.getClass(), "mUiAutomationBridge") .get(device); if (API_18) { controller = enableField(bridge.getClass().getSuperclass(), "mInteractionController").get(bridge); } else { controller = enableField(bridge.getClass(), "mInteractionController") .get(bridge); } }
因为uiautomator api的改动,在api18以上的版本中,mInteractionController是存在于UiAutomationBridge的父类中的变量,而在18以下的版本中它是存在于本类中的。所以反射时会有一点点小小点差异,但总的来说都是要获得InteractionController这个类,因为这个类里面存在有我们要但touch类但方法。最后我们就能轻松调用鼠标的TouchUp和TouchDown他们啦。然后再加上时间,长按就实现啦。
package io.appium.android.bootstrap.handler; import com.android.uiautomator.common.ReflectionUtils; import com.android.uiautomator.core.UiObjectNotFoundException; import io.appium.android.bootstrap.Logger; import java.lang.reflect.Method; /** * This handler is used to perform a touchDown event on an element in the * Android UI. * */ public class TouchDown extends TouchEvent { @Override protected boolean executeTouchEvent() throws UiObjectNotFoundException { printEventDebugLine("TouchDown"); try { final ReflectionUtils utils = new ReflectionUtils(); final Method touchDown = utils.getControllerMethod("touchDown", int.class, int.class); return (Boolean) touchDown.invoke(utils.getController(), clickX, clickY); } catch (final Exception e) { Logger.debug("Problem invoking touchDown: " + e); return false; } } }
有了上面的分析,对TouchUp和TouchDown还有TouchMove的分析就不用再多说了,都是反射的原理
类同
类同