Appium输入慢的原因分析
来源:互联网 发布:北京java培训中心 编辑:程序博客网 时间:2024/06/05 17:05
使用appium输入中文,发现好慢!至少5秒以上,如果在这样的情况下做测试,这就好悲剧了。
从appium(1.6.3)代码上来看,没有什么问题,直接是通过boostrap的setText的方法。说是就下载了appium-bootstrap的代码看,从这里开发找到的代码,都是java的代码,找到 io.appium.android.bootstrap.handler.SetText
在new Clear().execute(command);时间长达5秒(打日志发现),不管文本框有没有内容,都会执行
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package io.appium.android.bootstrap.handler;import com.android.uiautomator.core.UiDevice;import com.android.uiautomator.core.UiObjectNotFoundException;import com.android.uiautomator.core.UiSelector;import io.appium.android.bootstrap.*;import io.appium.android.bootstrap.exceptions.ElementNotFoundException;import io.appium.android.bootstrap.handler.Find;import org.json.JSONException;import java.util.Hashtable;/** * This handler is used to set text in elements that support it. * */public class SetText extends CommandHandler { /* * @param command The {@link AndroidCommand} used for this handler. * * @return {@link AndroidCommandResult} * * @throws JSONException * * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android. * bootstrap.AndroidCommand) */ @Override public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { AndroidElement el = null; if (command.isElementCommand()) { el = command.getElement(); Logger.debug("Using element passed in: " + el.getId()); } else { try { AndroidElementsHash elements = AndroidElementsHash.getInstance(); el = elements.getElement(new UiSelector().focused(true), ""); Logger.debug("Using currently-focused element: " + el.getId()); } catch (ElementNotFoundException e) { Logger.debug("Error retrieving focused element: " + e); return getErrorResult("Unable to set text without a focused element."); } } try { final Hashtable<String, Object> params = command.params(); boolean replace = Boolean.parseBoolean(params.get("replace").toString()); String text = params.get("text").toString(); boolean pressEnter = false; if (text.endsWith("\\n")) { pressEnter = true; text = text.replace("\\n", ""); Logger.debug("Will press enter after setting text"); } boolean unicodeKeyboard = false; if (params.get("unicodeKeyboard") != null) { unicodeKeyboard = Boolean.parseBoolean(params.get("unicodeKeyboard").toString()); } String currText = el.getText(); new Clear().execute(command); //不管有没有,这里都会执行 if (!el.getText().isEmpty()) { // clear could have failed, or we could have a hint in the field // we'll assume it is the latter Logger.debug("Text not cleared. Assuming remainder is hint text."); currText = ""; } if (!replace) { text = currText + text; } final boolean result = el.setText(text, unicodeKeyboard); if (!result) { return getErrorResult("el.setText() failed!"); } if (pressEnter) { final UiDevice d = UiDevice.getInstance(); d.pressEnter(); } return getSuccessResult(result); } catch (final UiObjectNotFoundException e) { return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage()); } catch (final Exception e) { // handle NullPointerException return getErrorResult("Unknown error"); } }}
然后,我们再看Clear的代码
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package io.appium.android.bootstrap.handler;import android.graphics.Rect;import android.os.SystemClock;import android.view.InputDevice;import android.view.KeyCharacterMap;import android.view.KeyEvent;import com.android.uiautomator.core.UiObject;import com.android.uiautomator.core.UiObjectNotFoundException;import com.android.uiautomator.core.UiSelector;import io.appium.android.bootstrap.AndroidCommand;import io.appium.android.bootstrap.AndroidCommandResult;import io.appium.android.bootstrap.AndroidElement;import io.appium.android.bootstrap.CommandHandler;import io.appium.android.bootstrap.Logger;import io.appium.android.bootstrap.WDStatus;import io.appium.uiautomator.core.InteractionController;import io.appium.uiautomator.core.UiAutomatorBridge;import org.json.JSONException;import java.lang.reflect.InvocationTargetException;/** * This handler is used to clear elements in the Android UI. * * Based on the element Id, clear that element. * * UiAutomator method clearText is flaky hence overriding it with custom implementation. */public class Clear extends CommandHandler { /* * Trying to select entire text with correctLongClick and increasing time intervals. * Checking if element still has text in them and and if true falling back on UiAutomator clearText * * @param command The {@link AndroidCommand} * * @return {@link AndroidCommandResult} * * @throws JSONException * * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android. * bootstrap.AndroidCommand) */ @Override public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { if (command.isElementCommand()) { try { final AndroidElement el = command.getElement(); // first, try to do native clearing Logger.debug("Attempting to clear using UiObject.clearText()."); el.clearText(); //无条件都会执行这块。然后再分析clearText if (el.getText().isEmpty()) { return getSuccessResult(true); } // see if there is hint text if (hasHintText(el)) { Logger.debug("Text remains after clearing, " + "but it appears to be hint text."); return getSuccessResult(true); } // next try to select everything and delete Logger.debug("Clearing text not successful. Attempting to clear " + "by selecting all and deleting."); if (selectAndDelete(el)) { return getSuccessResult(true); } // see if there is hint text if (hasHintText(el)) { Logger.debug("Text remains after clearing, " + "but it appears to be hint text."); return getSuccessResult(true); } // finally try to send delete keys Logger.debug("Clearing text not successful. Attempting to clear " + "by sending delete keys."); if (sendDeleteKeys(el)) { return getSuccessResult(true); } if (!el.getText().isEmpty()) { // either there was a failure, or there is hint text if (hasHintText(el)) { Logger.debug("Text remains after clearing, " + "but it appears to be hint text."); return getSuccessResult(true); } else if (!el.getText().isEmpty()) { Logger.debug("Exhausted all means to clear text but '" + el.getText() + "' remains."); return getErrorResult("Clear text not successful."); } } return getSuccessResult(true); } catch (final UiObjectNotFoundException e) { return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage()); } catch (final Exception e) { // handle NullPointerException return getErrorResult("Unknown error clearing text"); } } return getErrorResult("Unknown error"); } private boolean selectAndDelete(AndroidElement el) throws UiObjectNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Rect rect = el.getVisibleBounds(); // Trying to select entire text. TouchLongClick.correctLongClick(rect.left + 20, rect.centerY(), 2000); UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all")); if (selectAll.waitForExists(2000)) { selectAll.click(); } // wait for the selection SystemClock.sleep(500); // delete it UiAutomatorBridge.getInstance().getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0); return el.getText().isEmpty(); } private boolean sendDeleteKeys(AndroidElement el) throws UiObjectNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { String tempTextHolder = ""; // Preventing infinite while loop. while (!el.getText().isEmpty() && !tempTextHolder.equalsIgnoreCase(el.getText())) { // Trying send delete keys after clicking in text box. el.click(); // Sending delete keys asynchronously, both forward and backward for (int key : new int[] { KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_FORWARD_DEL }) { tempTextHolder = el.getText(); final int length = tempTextHolder.length(); final long eventTime = SystemClock.uptimeMillis(); KeyEvent deleteEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, key, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD); for (int count = 0; count < length; count++) { UiAutomatorBridge.getInstance().injectInputEvent(deleteEvent, false); } } } return el.getText().isEmpty(); } private boolean hasHintText(AndroidElement el) throws UiObjectNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { // to test if the remaining text is hint text, try sending a single // delete key and testing if there is any change. // ignore the off-chance that the delete silently fails and we get a false // positive. String currText = el.getText(); try { if (!el.getBoolAttribute("focused")) { Logger.debug("Could not check for hint text because the element is not focused!"); return false; } } catch (final Exception e) { Logger.debug("Could not check for hint text: " + e.getMessage()); return false; } InteractionController interactionController = UiAutomatorBridge.getInstance().getInteractionController(); interactionController.sendKey(KeyEvent.KEYCODE_DEL, 0); interactionController.sendKey(KeyEvent.KEYCODE_FORWARD_DEL, 0); return currText.equals(el.getText()); }}
再看看AndroidElement.clearText是什么样的
public void clearText() throws UiObjectNotFoundException { el.clearTextField(); }
这个都就是com.android.uiautomator.core.UiObject.clearTextField
于是找再找到uiautomator的代码再来分析(这个代码需要下载andriod sdk,在对应android版本的目录下,会有源码,也有uiautomator的源代码),我这里的路径是:
Android\sdk\sources\android-19\com\android\uiautomator\core
在UiObject.java找到clearTextField实现
/** * Clears the existing text contents in an editable field. * * The {@link UiSelector} of this object must reference a UI element that is editable. * * When you call this method, the method first sets focus at the start edge of the field. * The method then simulates a long-press to select the existing text, and deletes the * selected text. * * If a "Select-All" option is displayed, the method will automatically attempt to use it * to ensure full text selection. * * Note that it is possible that not all the text in the field is selected; for example, * if the text contains separators such as spaces, slashes, at symbol etc. * Also, not all editable fields support the long-press functionality. * * @throws UiObjectNotFoundException * @since API Level 16 */ public void clearTextField() throws UiObjectNotFoundException { Tracer.trace(); // long click left + center AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout()); if(node == null) { throw new UiObjectNotFoundException(getSelector().toString()); } Rect rect = getVisibleBounds(node); getInteractionController().longTapNoSync(rect.left + 20, rect.centerY()); //长按 // check if the edit menu is open UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all")); if(selectAll.waitForExists(50)) selectAll.click(); // wait for the selection SystemClock.sleep(250); //这里等250ms // delete it getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0); }
相信大家,可以找到慢的原因了。这里做一次长按,然再再做全选,然后再sleep(250),还有一个selectAll.waitForExists(50), 这些都是耗费时间的。
再找一下UiObject.java中setText的实现
public boolean setText(String text) throws UiObjectNotFoundException { Tracer.trace(text); clearTextField(); return getInteractionController().sendText(text); }
发现这里又调用了一次clearTextField,这样算来,设一次文本,都会清理两次文本,于是,这时间就长了。
优化:只需要将io.appium.android.bootstrap.handler.SetText中的new Clear().execute(command)去掉就可以了。
- Appium输入慢的原因分析
- Appium对于xpath 查找元素慢的原因
- Appium对于xpath 查找元素慢的原因
- 局域网速度变慢的原因分析
- 分析查询速度慢的原因
- 操作系统变慢的八大原因分析
- 开机启动慢的原因分析
- Android 开机启动慢的原因分析
- YGC越来越慢的原因分析
- 页面加载慢的原因分析
- appium的中文输入
- Appium Android测试中sendkeys 输入速度慢的替代方法
- Firefox启动慢的原因我的初步分析
- 数据库运行慢原因分析
- solr 启动慢原因分析
- 网站打开慢原因分析
- sql 查询慢的48个原因分析
- SQL查询慢的48个原因分析
- 前置和后置++
- Eclipse项目上传码云
- 【转载】一位资深程序员大牛给予Java初学者的学习路线建议
- Test
- python做音频格式转换
- Appium输入慢的原因分析
- WordPress 状态信息统计代码
- springMVC学习笔记
- Linux 用户操作
- java timeUnit 使用
- 文件复制
- 初始Cakephp,新手入门安装与配置
- androidの设计的布局在阿拉伯语下界面错乱的解决方法 总汇
- 英国“支付宝”也要搞VR支付了