我已经开始编写一个应用程序,它通过WebView为用户提供HTML表单.由于表单不在我的控制之下,填写的数据可以作为GET或POST请求发送.我的应用程序需要捕获传输的表单数据,即保持输入表单字段的内容.
使用来自WebViewClient的适当回调,例如onPageLoaded()
,很容易从GET请求中捕获表单数据.但是,我找不到任何适当的方法来允许POSTed数据相同,即能够访问包含表单数据的HTTP POST消息体.我在这里错过了相关的回调,或者根本没有办法用给定的API(即使是最新的8级)完成指定的目标?
假设不可能,我考虑覆盖和扩展android.webkit的部分,以便引入一个以某种方式传递POST主体的新回调钩子.这样,我的应用程序可以附带一个自定义浏览器/ WebViewClient,以实现所需的功能.但是,我在代码中找不到任何好的位置,并且会对这方面的任何提示感到高兴(如果方法看起来很有希望).
提前致谢!
正如我对原始问题的评论所指出的,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} } 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 + "})()"); } }
非正式地,它的作用是onsubmit
在页面完成加载时注入自定义JavaScript代码(作为处理程序).在提交表单时,Javascript将解析表单数据并通过JavaScriptInterface
对象将其传递回Java域.
为了解析表单字段,Javascript代码添加了表单onsubmit
和按钮onclick
处理程序.前者可以通过常规提交按钮处理规范表单提交,而后者处理自定义提交按钮,即在调用之前执行一些额外的Javascript魔术的按钮form.submit()
.
请注意,Javascript代码可能不完美:可能有其他方法提交我注入的代码可能无法捕获的表单.但是,我确信注入的代码可以更新以处理这些可能性.
提供的答案给出了错误,所以我决定做一个更简单的实现,其中还包含结构良好的JavaScript(意思是JS在一个文件中):
在您的assets文件夹中,创建一个名为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/fragment应该如下所示:
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(); }