robotium框架支持WebView,在robotium中有getWebElements()、getWebElements(By by)等方法來獲取android中的WebView的元素,並提供了 clickOnWebElement方法來完成點擊事件.android中的原生控件是比較好攻取的,那麼對於WebView這個框架是怎麼獲取的呢。javascript
第一步:利用JS獲取頁面中的全部元素 html
在PC上,獲取網頁的元素能夠經過注入javascript元素來完成,以Chrome瀏覽器爲例,打開工具——JavaScript控制檯(快捷方式:Ctrl+Shift+J),輸入 javascript:prompt(document.URL)即會彈出含當前頁面的URL的提示框,所以經過編寫適當的JS腳本是能夠在這個彈出框中顯示全部頁面元素的。RobotiumWeb.js就是此功能實現用的JS腳本。以solo中getWebElements()爲例,java
[java] view plaincopyandroid
public ArrayList<WebElement> getWebElements(boolean onlySufficientlyVisible){ web
boolean javaScriptWasExecuted = executeJavaScriptFunction("allWebElements();"); 瀏覽器
return getWebElements(javaScriptWasExecuted, onlySufficientlyVisible); app
} 框架
[java] view plaincopyide
private boolean executeJavaScriptFunction(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 = prepareForStartOfJavascriptExecution();
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] view plaincopy
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] view plaincopy
@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.createWebElementAndAddInList(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] view plaincopy
public void enableJavascriptAndSetRobotiumWebClient(List<WebView> 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中
獲取到了prompt提示框中的消息後,接下來就是對這些已通過處理含特殊格式的消息進行解析處理了,依次獲得WebElement的id、text、name等字段。
[java] view plaincopy
private WebElement createWebElementAndSetLocation(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<String, String> attributes = new Hashtable<String, String>();
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] view plaincopy
/**
* Sets the location of a {@code WebElement}
*
* @param webElement the {@code TextView} object to set location
* @param webView the {@code WebView} the text is shown in
* @param x the x location to set
* @param y the y location to set
* @param width the width to set
* @param height the height to set
*/
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);
}
至此,WebElement對象中包含了id、text、name等字段,還包含了x、y座標,知道了座標後就能夠像其它Android中的原生View同樣根據座標發送點擊事件。