Используя SDK WebView, вы можете легко перехватывать параметры GET, создавая дочерний класс WebViewClient и переопределяя метод shouldOverrideUrlLoading. Это простой и широко описанный подход, который вы можете найти повсюду.

Но что, если вам нужно получить доступ к данным формы, т.е. прочитать введенные используемые данные в форме метода публикации WebView. У меня возникла такая потребность, когда я работал над внедрением платежной системы, и мне нужно было извлечь данные со страницы результатов оплаты и передать их на сервер, и эти данные были переданы через форму как почтовый запрос.

Мне не удалось сделать это с помощью WebViewClient#shouldOverrideUrlLoading, и я думал, что у Android должно быть что-то под капотом, но я ничего не нашел в SDK.

Во время исследования я нашел несколько статей, основанных на работе с XMLHttpRequest, и я нашел их немного запутанными. В конце дам ссылку на такую ​​реализацию, может кому тоже поможет. В конечном итоге я наткнулся на довольно простое решение, основанное на нескольких шагах:

  • Реализовать метод JavaScript для чтения данных формы.
  • Включите JavaScript в webView.settings.
  • Запустите метод для получения данных формы и извлеките их

Он выглядел надежным и простым, и после нескольких тестов я смог извлечь данные формы с веб-страницы. Я решил сгруппировать решение вместе и поделиться им. Здесь вы можете найти все необходимое для выполнения такой задачи, также я подготовил пример с чтением формы Google, с которой вы можете поиграть.

Я предоставлю ссылку на GitHub ниже.

Выполнение

Создайте класс интерфейса JavaScript. Подробнее об интерфейсе javascript вы можете узнать, например, в официальной документации. Проще говоря, он позволяет соединить javascript, работающий в WebView, с кодом вашего Android, написанным на Kotlin или Java. Ключевую роль здесь играет аннотация @JavaScriptInterface.

class FormDataWebViewFormDataJSInterface {

    private val formDataMap = mutableMapOf<String, String>()

    fun getFormData(): Map<String, String> = formDataMap

    fun resetFormData() = formDataMap.clear()

    @JavascriptInterface
    fun processFormData(data: String) {
        val values = data.split("&".toRegex()).toTypedArray()
        for (pair in values) {
            val formKey = pair.substring(
                startIndex = 0,
                endIndex = pair.indexOfFirst { it == '=' }
            )
            val formValue = pair.substring(
                startIndex = pair.indexOfFirst { it == '=' } + 1,
                endIndex = pair.length
            )
            formDataMap[formKey] = formValue
        }
    }
}

Создайте метод JavaScript для чтения формы.

function parseForm(form){
    var values='';
    for (var i=0; i<form.elements.length; i++) {
        values+=form.elements[i].name+'='+form.elements[i].value+'&'
    }
    window.$FORM_READER_JS_INTERFACE.processFormData(values);
}

Мы берем все элементы формы и перемещаем их в одно строковое значение следующего формата: formName=formValue&otherFormName=otherFormValue. Это позволяет сгруппировать все значения элементов формы вместе для строки и проанализировать их позже.

Обратите внимание, что FORM_READER_JS_INTERFACE — это имя, используемое для регистрации интерфейса javascript, и processFormData должно совпадать с именем метода Kotlin, аннотированным @JavaScriptInterface. Вы можете назвать его как хотите, просто убедитесь, что его имя одинаково в JS и в коде Kotlin.

Включите Javascript и зарегистрируйте интерфейс JavaScript.

val javaScriptInterface = FormDataWebViewFormDataJSInterface()
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(
    javaScriptInterface,
    FORM_READER_JS_INTERFACE
)

И последний шаг: вам нужно вызвать JS-метод через evaluateJavascriptcall на странице, где вам нужно получить доступ к данным формы. Метод parseForm был описан выше. Мы создаем и запускаем анонимную JS-функцию, которая читает все формы документов и вызывает parseForm для каждой из них.

webView.evaluateJavascript(
    """
function parseForm(form){
  var values='';
  for (var i=0; i<form.elements.length; i++) {
    values+=form.elements[i].name+'='+form.elements[i].value+'&'
  } 
  window.$FORM_READER_JS_INTERFACE.processFormData(values);
}
    
(function() { 
  for (var i=0 ; i< document.forms.length ; i++){
    parseForm(document.forms[i]);
  };
})()
 """.trimMargin()
)

Рутирование этого метода Javascript запускает processFormData(data: String), определенный внутри FormDataWebViewFormDataJSInterface. Это возможно, потому что мы ранее зарегистрировали FORM_READER_JS_INTERFACE через вызов метода addJavascriptInterface.

Метод Kotlin processFormData, в свою очередь, будет считывать строковое значение, разделять его на &и перемещать пары "ключ-значение" в определенную карту, к которой мы сможем получить доступ после оценки Javascript. призыв и исполнение.

Стоит отметить, что AssessmentJavascript вызывается асинхронно, он выполняется в так называемом разделенном потоке Java-Bridge, т.е. вне mainThread. Чтобы получить доступ к данным сразу после выполнения JS, используйте, например, ResultCallback, определенный в методе evaluateJavascript в качестве вторичного параметра.

evaluateJavascript(JAVASCRIPT_READ_FORM_METHOD.trimMargin()) {
  val data: Map<String, String> =javaScriptInterface.getFormData()
}

Здесь JAVASCRIPT_READ_FORM_METHOD — тело метода JS, описанное выше. Собственно все, результирующая карта будет содержать все элементы формы со своим значением.

Я подготовил небольшое демо с гугл-формами, с которыми вы можете поиграться на GitHub.

И вот несколько ссылок, которые я упомянул в статье





Спасибо за чтение, надеюсь, это поможет.