我已经开始编写一个应用程序,它通过WebView为用户提供HTML表单。由于表单不在我的控制之下,填写的数据可以作为GET或POST请求发送。我的应用程序需要捕获传输的表单数据,即保留输入表单字段的内容。
使用来自WebViewClient(例如onPageLoaded()
)的足够回调,可以轻松地从GET请求中捕获表单数据。但是,我找不到任何适当的方法来允许POSTed数据相同,即能够访问包含表单数据的HTTP POST消息体。我在这里错过了相关的回调,或者根本没有办法用给定的API(即使是最新的8级)完成指定的目标?
假设不可能,我考虑覆盖并扩展android.webkit的部分内容,以便引入一个以某种方式传递给POST主体的新回调钩子。这样,我的应用程序可以附带一个自定义浏览器/ WebViewClient,以实现所需的功能。但是,我在代码中找不到任何好的位置,并且会对这方面的任何提示感到高兴(如果方法看起来很有希望的话)。
提前致谢!
答案 0 :(得分:22)
正如我对原始问题的评论所示,JavaScript注入方法有效。基本上,您需要做的是将一些JavaScript代码添加到DOM onsubmit事件中,让它解析表单的字段,并将结果返回给Java注册的函数。
代码示例:
public class MyBrowser extends Activity {
private final String jsInjectCode =
"function parseForm(event) {" +
" var form = this;" +
" // make sure form points to the surrounding form object if a custom button was used" +
" if (this.tagName.toLowerCase() != 'form')" +
" form = this.form;" +
" var data = '';" +
" if (!form.method) form.method = 'get';" +
" data += 'method=' + form.method;" +
" data += '&action=' + form.action;" +
" var inputs = document.forms[0].getElementsByTagName('input');" +
" for (var i = 0; i < inputs.length; i++) {" +
" var field = inputs[i];" +
" if (field.type != 'submit' && field.type != 'reset' && field.type != 'button')" +
" data += '&' + field.name + '=' + field.value;" +
" }" +
" HTMLOUT.processFormData(data);" +
"}" +
"" +
"for (var form_idx = 0; form_idx < document.forms.length; ++form_idx)" +
" document.forms[form_idx].addEventListener('submit', parseForm, false);" +
"var inputs = document.getElementsByTagName('input');" +
"for (var i = 0; i < inputs.length; i++) {" +
" if (inputs[i].getAttribute('type') == 'button')" +
" inputs[i].addEventListener('click', parseForm, false);" +
"}" +
"";
class JavaScriptInterface {
@JavascriptInterface
public void processFormData(String formData) {
//added annotation for API > 17 to make it work
<do whatever you need to do with the form data>
}
}
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.browser);
WebView browser = (WebView)findViewById(R.id.browser_window);
browser.getSettings().setJavaScriptEnabled(true);
browser.addJavascriptInterface(new JavaScriptInterface(), "HTMLOUT");
browser.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:(function() { " +
MyBrowser.jsInjectCode + "})()");
}
}
非正式地,这样做是在页面完成加载时注入自定义JavaScript代码(作为onsubmit
处理程序)。在提交表单时,Javascript将解析表单数据并通过JavaScriptInterface
对象将其传递回Java域。
为了解析表单字段,Javascript代码添加了表单onsubmit
和按钮onclick
处理程序。前者可以通过常规提交按钮处理规范表单提交,而后者处理自定义提交按钮,即在调用form.submit()
之前执行一些额外Javascript魔法的按钮。
请注意,Javascript代码可能不完美:可能还有其他方法可以提交我注入的代码可能无法捕获的表单。但是,我确信注入的代码可以更新以处理这些可能性。
答案 1 :(得分:7)
提供的答案给出了错误,所以我决定做一个更简单的实现,其中还包含结构良好的JavaScript(意思是JS在文件中):
在您的资源文件夹中,创建一个名为inject.js的文件,其中包含以下代码:
document.getElementsByTagName('form')[0].onsubmit = function () {
var objPWD, objAccount, objSave;
var str = '';
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].name.toLowerCase() === 'username') {
objAccount = inputs[i];
} else if (inputs[i].name.toLowerCase() === 'password') {
objPWD = inputs[i];
} else if (inputs[i].name.toLowerCase() === 'rememberlogin') {
objSave = inputs[i];
}
}
if(objAccount != null) {
str += objAccount.value;
}
if(objPWD != null) {
str += ' , ' + objPWD.value;
}
if(objSave != null) {
str += ' , ' + objSave.value;
}
window.AndroidInterface.processHTML(str);
return true;
};
这是我们用于注入的javascript代码,您可以根据需要切换if语句,而不是使用类型或名称。回调Android就是这一行:window.AndroidInterface.processHTML(str);
然后您的Activity /片段应如下所示:
public class MainActivity extends ActionBarActivity {
class JavaScriptInterface {
@JavascriptInterface
public void processHTML(String formData) {
Log.d("AWESOME_TAG", "form data: " + formData);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
this.setContentView(webView);
// enable javascript
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JavaScriptInterface(), "AndroidInterface");
// catch events
webView.setWebViewClient(new WebViewClient(){
@Override
public void onPageFinished(WebView view, String url) {
try {
view.loadUrl("javascript:" + buildInjection());
} catch (IOException e) {
e.printStackTrace();
}
}
});
webView.loadUrl("http://someurl.com");
}
private String buildInjection() throws IOException {
StringBuilder buf = new StringBuilder();
InputStream inject = getAssets().open("inject.js");// file from assets
BufferedReader in = new BufferedReader(new InputStreamReader(inject, "UTF-8"));
String str;
while ((str = in.readLine()) != null) {
buf.append(str);
}
in.close();
return buf.toString();
}