Java启动WinAppDriver操作Windows文件选择器时并发超时异常求助
问题场景
手动从文件资源管理器启动WinAppDriver时,所有操作完全正常;但通过Java代码启动WinAppDriver后,执行文件选择器操作会触发并发超时异常导致程序中止。此时若在Eclipse中点击停止按钮,文件名却能正确输入到文件选择器中。怀疑是Java进程阻碍了WindowsDriver发送数据,求验证及解决方案。
依赖环境
- selenium-java 4.5.0
- java-client 8.1.1(升级到后续版本会出现ImmutableMap转Capabilities失败的错误)
- 必须依赖Appium以配合Selenium Grid使用
相关代码
获取WinAppDriver的代码
public static WindowsDriver getDriver() { try { if (!checkForRunning()) { String executable = Properties.getProperty("test.winappdriver.location", winAppDriver); Runtime.getRuntime().exec(executable); Thread.sleep(10000); } } catch (Exception ex) { throw new AssertionError(ex.getMessage(), ex); } HttpCommandExecutor exec = null; try { exec = new HttpCommandExecutor(new URL("http://127.0.0.1:4723/")); } catch (MalformedURLException e) { throw new AssertionError(e.getMessage(), e); } DesiredCapabilities dcaps = new DesiredCapabilities(); dcaps.setCapability("automationName", "Windows"); dcaps.setCapability("app", "Root"); dcaps.setCapability("platformName", "Windows"); dcaps.setCapability("deviceName", "Windows PC"); WindowsDriver app = new WindowsDriver(exec, dcaps); return app; }
代码逻辑:检查WinAppDriver是否运行,未运行则启动并固定等待10秒。
操作Windows文件选择器的代码
List<WebElement> g = wdriver.findElements(By.name("File name:")); if (g.size() < 2) { System.out.println("*** retry"); g = wdriver.findElements(By.name("File name:")); } g.get(0).sendKeys(" "); WebElement clickIt = g.get(1); clickIt.click(); String curdir = System.getProperty("user.dir"); // add Keys.ENTER to file name to close it, since the OK button is hard to find String f2 = StringUtils.join(curdir, "\"", fileName, Keys.ENTER); clickIt.sendKeys(f2);
问题分析与解决办法
关于Java进程阻碍的验证
你的怀疑成立:Runtime.getRuntime().exec(executable)启动WinAppDriver后,子进程的标准输出/错误流如果未被消费,会导致缓冲区填满,进而阻塞子进程运行,间接打断WinAppDriver与Java客户端的通信链路,最终引发超时。手动启动时无此问题,是因为子进程输出流关联到系统控制台,不会被阻塞。
针对性解决方案
消费WinAppDriver进程的输出流
修改启动逻辑,添加线程消费子进程的输出/错误流,避免缓冲区阻塞:Process winAppDriverProcess = Runtime.getRuntime().exec(executable); // 启动线程消费标准输出 new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(winAppDriverProcess.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("WinAppDriver Log: " + line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 启动线程消费错误输出 new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(winAppDriverProcess.getErrorStream()))) { String line; while ((line = reader.readLine()) != null) { System.err.println("WinAppDriver Error: " + line); } } catch (IOException e) { e.printStackTrace(); } }).start();同时替换固定等待
Thread.sleep(10000)为端口连通性轮询,更可靠地等待WinAppDriver启动完成:int port = 4723; long timeout = System.currentTimeMillis() + 15000; boolean isPortOpen = false; while (System.currentTimeMillis() < timeout && !isPortOpen) { try (Socket socket = new Socket("127.0.0.1", port)) { isPortOpen = true; } catch (IOException ignored) { Thread.sleep(1000); } } if (!isPortOpen) { throw new AssertionError("WinAppDriver failed to start within timeout"); }确保进程权限
以管理员权限启动Eclipse(右键选择"Run as Administrator"),WinAppDriver需要管理员权限才能正常控制系统级窗口(如文件选择器),代码启动时会继承Java进程的权限,权限不足会导致通信异常。优化文件选择器操作逻辑
替换简单重试为显式等待,同时修正路径拼接逻辑:WebDriverWait wait = new WebDriverWait(wdriver, Duration.ofSeconds(10)); List<WebElement> fileInputs = wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.name("File name:"), 1)); WebElement inputField = fileInputs.get(1); inputField.clear(); // 直接拼接路径,避免StringUtils.join的参数错误 String fullPath = curdir + "\\" + fileName; inputField.sendKeys(fullPath + Keys.ENTER);版本兼容性校验
确保Selenium Grid版本与selenium-java 4.5.0完全匹配,Appium服务器版本(若使用)需兼容java-client 8.1.1,版本不匹配也会引发通信超时问题。
内容的提问来源于stack exchange,提问作者Tony

