Используя 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.
И вот несколько ссылок, которые я упомянул в статье
Спасибо за чтение, надеюсь, это поможет.