2010-08-31 113 views
16

我已经开始编写一个应用程序,它通过WebView向用户提供HTML表单。由于表单不在我的控制之下,填充的数据可能会以GET或POST请求的形式发送。我的应用程序需要捕获运输的表单数据,也就是说,抓住输入表单域的内容。是否可以访问通过WebView发布的HTML表单数据?

从WebViewClient使用足够的回调函数(如onPageLoaded()),可以很容易地从GET请求中捕获表单数据。但是,我找不到任何适当的方法来允许POST数据相同,即能够访问包含表单数据的HTTP POST消息主体。我是否错过了一个相关的回调,或者根本没有办法用给定的API来完成指定的目标(甚至是最新的8级)?

假设这是不可能的,我考虑重写和扩展android.webkit的一部分,以便引入一个新的回调钩子,以便以某种方式传递POST主体。这样,我的应用程序就可以与自定义的浏览器/ WebViewClient一起发货,它可以满足所需的功能。然而,我从代码中找不到任何合适的位置,并且对于这方面的任何提示感到高兴(如果方法看起来很有希望)。

在此先感谢!

+0

其实,我可能已经找到了一种方法自己:这应该有可能注入一些JavaScript代码解析当用户提交表单时显示字段。所提供的Javascript到Java接口应该能够将填充的数据传回到Java空间进行进一步处理。 将尝试该方法并回报。当然,任何进一步的反馈仍然是欢迎的! – 2010-09-01 01:24:42

回答

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代码可能并不完美:可能有其他方法来提交表单,我的注入代码可能无法捕捉。不过,我相信注入的代码可以更新来处理这种可能性。

+0

'window.HTMLOUT.processFormData(data);'不适用于我,但只是'HTMLOUT.processFormData(data);'为我工作。 – flexdroid 2013-05-23 10:02:39

+0

不错....感谢的人 – Noman 2013-09-22 12:05:59

+0

好吧,我finnaly得到它。更正代码也 – 2013-10-14 00:25:59

6

所提供的答案给出了错误,所以我决定做一个简单的实现也有特色的结构良好的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);

那么你的活动/片段应该是这样的:

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(); 
    } 
+0

你从哪里得到输入框的值 – 2016-12-01 14:27:41

相关问题