我有一个Web服务,正在尝试使用WebView在后台进行身份验证。当我最初发送请求时,它将正常运行(基于凭据的失败/成功),但是在接收到缓存的响应之后。
这是我的webview设置代码:
WebView browser = new WebView(this);
WebSettings settings = browser.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setAppCacheEnabled(false);
browser.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
Log.d("BROWSERPROGRESS", Integer.toString(progress));
}
});
jsInterface = new AddAccountJSInterface();
browser.addJavascriptInterface(jsInterface, "ADDACCOUNTJSINTERFACE");
browser.setWebViewClient(new AddAccountClient(this));
如您所见,我还有两个控制我的webView的类:
另外,我确实有一个WebChromeClient,但它仅用于调试,我很确定它不会干扰任何东西。
JS接口(interface)只是提供了一种获取正文HTML进行执行分析的简单方法,因此我相信这也不是问题。
WebViewClient中包含以下代码,该代码根据来自Web服务的各种响应完成路由的大多数“自定义”工作。
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.contains(INSTALL_PREFIX)) {
HashMap<String, String> params = extractParameters(url);
verificationComplete(params);
return true;
}
return false;
}
@Override
public void onPageFinished(WebView view, String url){
if(invalidShop(view)) {
Toast.makeText(context, context.getString(R.string.no_find_shop), Toast.LENGTH_SHORT).show();
shopAddressField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else if(url.contains(ADMIN_AUTH_LOGIN)) {
if(invalidLogin(view)) {
Toast.makeText(context, context.getString(R.string.invalid_login),Toast.LENGTH_SHORT).show();
emailField.requestFocus();
replaceUiElements(loadingBar, addAccountButton);
} else {
String email = emailField.getText().toString();
String password = passwordField.getText().toString();
String submitJS = String.format(FORM_SUBMISSION_JS, email, password);
jsInterface.setInnerHTML("");
browser.loadUrl(submitJS);
}
}
}
在我的 Activity 中,我需要填写3个文本字段,然后单击一个按钮将其提交。然后,该 Activity 从3个文本字段(shopAddressField,usernameField,passwordField)中获取数据,然后执行一些JavaScript以填充一些表单数据(已加载到不可见的webView中),然后单击Submit按钮。
这是困惑的最后一部分,它似乎是在缓存来自服务器的响应(也许正在使用cookie?)并返回该值,而不是询问服务器数据是否正确。
一点澄清:
JSInterface只是一个Java对象,允许我在Webview上执行与该对象内的函数绑定(bind)的javascript。在我的情况下,我的JSInterface有一个函数是setInnerHtml(String html)。
这是在webview上执行的javascript:
javascript:window.ADDACOUNTJSINTERFACE.setInnerHTML(document.body.innerHTML)
这是setInnerHtml函数:
public void setInnerHtml(String innerHtml) {
this.innerHtml = innerHtml;
}
因此,当我实际执行jsInterface.setInnerHtml(“”)时,我只是在重写被拉入的HTML(以确保出于某种原因我没有从那里得到旧数据)。
至于我的SubmitJS,再次在我的webView上执行一些Javascript,如下所示:
// submitJS will be something like this once all the credentials have been set
// Note: I know that the server will make jQuery available
// Note: Much of the Java string formatting has been removed to help clarify
// the code.
String submitJS =
"javascript:(function() {
$('login-input').value='username';
$('password').value='password';
$('sign-in-form').up().submit();
})()"
// I then simply get the webview to execute the javascript above
webView.loadData(submitJS);
最佳答案
因此,事实证明问题并非基于缓存,也可能不是cookie。
在webView上执行javascript时,它会在单独的线程中执行此操作,并且可能会很慢。这导致竞争状态,导致代码以错误的顺序执行。
我已经通过使用信号量作为互斥体解决了这个问题。这使我可以防止getter在webView上的Javascript执行之前返回。
我现在创建的界面如下所示:
private class AddAccountJSInterface {
private final String TAG = getClass().getName().toUpperCase();
private Semaphore mutex = new Semaphore(1, false);
private String innerHTML;
public void aquireSemaphore() {
Log.d(TAG, "Attempting to lock semaphore");
try {
mutex.acquire();
} catch(InterruptedException e) {
Log.d(TAG, "Oh snap, we got interrupted. Just going to abort.");
return;
}
Log.d(TAG, "Semaphore has been aquired");
}
@SuppressWarnings("unused")
public void setInnerHTML(String html) {
this.innerHTML = html;
Log.d(TAG, "setInnerHTML is now releasing semaphore.");
mutex.release();
Log.d(TAG, "setInnerHTML has successfully released the semaphore.");
}
public synchronized String getInnerHTML() {
Log.d(TAG, "getInnerHTML attempting to aquire semaphore, may block...");
String innerHTML = "";
try {
mutex.acquire();
Log.d(TAG, "getInnerHTML has aquired the semaphore, grabbing data.");
innerHTML = this.innerHTML;
Log.d(TAG, "getInnerHTML no longer needs semaphore, releasing");
mutex.release();
} catch (InterruptedException e) {
Log.d(TAG, "Something has gone wrong while attempting to aquire semaphore, aborting");
}
return innerHTML;
}
}
现在,我在代码中使用它的方式如下:
// I have access to the jsInterface object which is an instance of the class above as well as a webView which I will be executing the javascript on.
String getInnerHtmlJS = "javascript:window.MYJSINTERFACE.setInnerHTML(document.body.innerHTML);"
jsInterface.aquireSemaphore()
// Execute my JS on the webview
jsInterface.loadUrl(getInnerHtmlJS)
// Now we get our inner HTML
// Note: getInnerHTML will block since it must wait for the setInnerHTML (executed via the JS) function to release the semaphore
String theInnerHTML = jsInterface.getInnerHTML();