robotium原理之获取WebElement元素
转自:http://blog.csdn.net/hunterno4/article/details/35569665
第一步:利用JS获取页面中的所有元素
在PC上,获取网页的元素可以通过注入javascript元素来完成,以Chrome浏览器为例,打开工具——JavaScript控制台(快捷方式:Ctrl+Shift+J),输入
javascript:prompt(document.URL)即会弹出含当前页面的URL的提示框,因此通过编写适当的JS脚本是可以在这个弹出框中显示所有页面元素的。RobotiumWeb.js就是此功能实现用的JS脚本。以solo中getWebElements()为例,
[java]
public ArrayList
getWebElements(boolean
onlySufficientlyVisible){
- boolean javaScriptWasExecuted =
executeJavaScriptFunctio
n( "allWebElements();"); - return getWebElements(javaScriptWasExecuted, onlySufficientlyVisible);
- }
[java]
- private boolean
executeJavaScriptFunctio
n( final String function){ - final WebView webView = viewFetcher.getFreshestView(viewFetcher.getCurrentViews(WebView.class, true));
- if(webView == null){
- return false;
- }
- //做一些JS注入执行前的准备工作,例如将WebView设为可允许执行JS等,并将RobotiumWeb.js中的脚本以String形式返回
- final String javaScript =
prepareForStartOfJavascr
iptExecution(); - activityUtils.getCurrentActivity(false).runOnUiThread(new Runnable() {
- public void run() {
- if(webView != null){
- webView.loadUrl("javascript:" + javaScript + function);
- }
- }
- });
- return true;
- }
可以看出这个方法执行的是allWebElements();函数,即类似执行RobotiumWeb.js文件中如下JS代码片段:
可以把如下片段放到JavaScript控制台中看效果
javascript:
- function allWebElements() {
- for (var key in document.all){
- try{
- promptElement(document.all[key]); //调用promptElement(element)函数
- }catch(ignored){}
- }
- finished(); //执行完后,调用finished()函数
- }
- function promptElement(element) {
- var id = element.id;
- var text = element.innerText;
- if(text.trim().length == 0){
- text = element.value;
- }
- var name = element.getAttribute('name');
- var className = element.className;
- var tagName = element.tagName;
- var attributes = "";
- var htmlAttributes = element.attributes;
- for (var i = 0, htmlAttribute; htmlAttribute = htmlAttributes[i]; i++){
- attributes += htmlAttribute.name + "::" + htmlAttribute.value;
- if (i + 1 < htmlAttributes.length) {
- attributes += "#$";
- }
- }
- var rect = element.getBoundingClientRect();
- if(rect.width > 0 && rect.height > 0 && rect.left >= 0 && rect.top >= 0){
- prompt(id + ';,' + text + ';,' + name + ";," + className + ";," + tagName + ";," + rect.left + ';,' + rect.top + ';,' + rect.width + ';,' + rect.height + ';,' + attributes); //弹出包含id、text、name等字段的提示框
- }
- }
- function finished(){
- prompt('robotium-finished'); //弹出包含robotium-finished字符串的提示框,用于标识脚本注入执行结束
- }
从脚本中可以看出JS获得页面元素后还进行了一定的格式化处理,在每个元素之间加了;,符号,这也是为了在后面代码中更加方便地解析。脚本的最后调用了finished()函数,即弹出包含robotium-finished的提示框。这一步完成了页面元素的获取,那么提示框中包含的内容在Android中怎么获取呢?
第二步:在Android中获取WebView中prompt提示框中的信息
在Android的Webkit包中有个WebChromeClient类,这个类中的onJsPrompt方法就是用于处理WebView中的提示框的,当WebView中有JS提示框时,会回调该方法,String
message参数将包含提示框中的信息,因此robotium写了个继承自WebChromeClient类的RobotiumWebClient类。覆写了onJsPrompt
onJsPrompt(WebView view, String url, String message, String defaultValue,
JsPromptResult result) |
[java]
- @Override
- public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
- if(message != null && (message.contains(";,") || message.contains("robotium-finished"))){
- //如果提示框中包含robotium-finished字符串,即表示那段JS注入脚本执行完毕了
- if(message.equals("robotium-finished")){
- webElementCreator.setFinished(true);
- }
- else{
- webElementCreator.createWebElementAndAddIn
List(message, view);//有人提示框中的内容,那么就可以对提示框中的内容进行处理了 - }
- r.confirm();
- return true;
- }
- else {
- if(originalWebChromeClient != null) {
- return originalWebChromeClient.onJsPrompt(view, url, message, defaultValue, r);
- }
- return true;
- }
- }
另外,原本的WebView默认是不允许执行JS的,因此需要先执行enableJavascriptAndSetRobotiumWebClient方法。将JavaScriptEnabled设置为true,将将WebChromeClient设置为robotiumWebClient
[java]
- public void
enableJavascriptAndSetRo
botiumWebClient(List webViews, WebChromeClient originalWebChromeClient){ - this.originalWebChromeClient = originalWebChromeClient;
- for(final WebView webView : webViews){
- if(webView != null){
- inst.runOnMainSync(new Runnable() {
- public void run() {
- webView.getSettings().setJavaScriptEnabled(true);
- webView.setWebChromeClient(robotiumWebClient);
- }
- });
- }
- }
- }
第三步:将提示框中的消息存入WebElement Java
bean中
[java]
- private WebElement
createWebElementAndSetLo
cation(String information, WebView webView){ - String[] data = information.split(";,"); //将消息按;,符号分割,其中;,符号是在前面执行JS时加入的
- String[] elements = null;
- int x = 0;
- int y = 0;
- int width = 0;
- int height = 0;
- Hashtable attributes = new Hashtable();
- try{
- x = Math.round(Float.valueOf(data[5]));
- y = Math.round(Float.valueOf(data[6]));
- width = Math.round(Float.valueOf(data[7]));
- height = Math.round(Float.valueOf(data[8]));
- elements = data[9].split("\\#\\$");
- }catch(Exception ignored){}
- if(elements != null) {
- for (int index = 0; index < elements.length; index++){
- String[] element = elements[index].split("::");
- if (element.length > 1) {
- attributes.put(element[0], element[1]);
- } else {
- attributes.put(element[0], element[0]);
- }
- }
- }
- WebElement webElement = null;
- try{
- webElement = new WebElement(data[0], data[1], data[2], data[3], data[4], attributes);//将id、text、name等字段存入
- setLocation(webElement, webView, x, y, width, height);
- }catch(Exception ignored) {}
- return webElement;
- }
[java]
- private void setLocation(WebElement webElement, WebView webView, int x, int y, int width, int height ){
- float scale = webView.getScale();
- int[] locationOfWebViewXY = new int[2];
- webView.getLocationOnScreen(locationOfWebViewXY);
- int locationX = (int) (locationOfWebViewXY[0] + (x + (Math.floor(width / 2))) * scale);
- int locationY = (int) (locationOfWebViewXY[1] + (y + (Math.floor(height / 2))) * scale);
- webElement.setLocationX(locationX);
- webElement.setLocationY(locationY);
- }
网页:http://www.robotium.cn/archives/239,也提到了一种方法:
public void testAddJsToWebView(){
class ExecObj{
public void showMessage(){
Toast.makeText(solo.getCurrentActivity(), "注入js,由js处理并调用ExecObj对象的方法!", Toast.LENGTH_LONG).show();
}
}
solo.sleep(1000);
//获得webview控件
webView = (WebView) solo.getView(R.id.webView);
webView.loadUrl("http://www.robotium.cn");
solo.sleep(4000);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new ExecObj(), "injectedObject");
webView.loadUrl("javascript:injectedObject.showMessage()");
solo.sleep(10000);
}
----夫英雄者,胸怀大志,腹有良谋,有包藏宇宙之机,吞吐天地之志者也。