Создание веб-приложений в WebView

Используйте WebView для отображения веб-приложения или веб-страницы в составе клиентского приложения. Класс WebView — это расширение класса View в Android, позволяющее отображать веб-страницы в рамках макета вашей активности. Он не включает в себя функции полноценного веб-браузера, такие как элементы управления навигацией или адресная строка. По умолчанию WebView отображает только веб-страницу.

WebView поможет вам предоставить в вашем приложении информацию, которую может потребоваться обновить, например, соглашение с конечным пользователем или руководство пользователя. В вашем приложении для Android вы можете создать Activity , содержащую WebView , а затем использовать его для отображения документа, размещённого в сети.

WebView также может быть полезен, когда ваше приложение предоставляет пользователю данные, для извлечения которых требуется подключение к Интернету, например, электронная почта. В этом случае может оказаться проще создать WebView в вашем приложении для Android, который отображает веб-страницу со всеми пользовательскими данными, чем выполнять сетевой запрос, а затем анализировать данные и отображать их в макете Android. Вместо этого вы можете разработать веб-страницу, адаптированную для устройств на базе Android, а затем реализовать WebView в вашем приложении для Android, который будет загружать эту веб-страницу.

В этом документе описывается, как начать работу с WebView , как связать JavaScript с вашей веб-страницы с клиентским кодом в вашем приложении Android, как осуществлять навигацию по страницам и как управлять окнами при использовании WebView .

Работа с WebView на более ранних версиях Android

Чтобы безопасно использовать новейшие возможности WebView на устройстве, на котором работает ваше приложение, добавьте библиотеку AndroidX Webkit . Это статическая библиотека, которую вы можете добавить в своё приложение для использования API android.webkit , недоступных в более ранних версиях платформы.

Добавьте его в файл build.gradle следующим образом:

Котлин

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

Круто

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

Для получения более подробной информации изучите пример WebView на GitHub.

Добавьте WebView в свое приложение

Чтобы добавить WebView в свое приложение, вы можете включить элемент <WebView> в макет активности или настроить все окно Activity как WebView в onCreate() .

Добавьте WebView в макет активности

Чтобы добавить WebView в макет вашего приложения, добавьте следующий код в XML-файл макета вашей активности:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

Чтобы загрузить веб-страницу в WebView , используйте loadUrl() , как показано в следующем примере:

Котлин

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Ява

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

Добавьте WebView в onCreate()

Чтобы добавить WebView в свое приложение в методе onCreate() действия, используйте логику, подобную следующей:

Котлин

val myWebView = WebView(activityContext)
setContentView(myWebView)

Ява

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

Затем загрузите страницу:

Котлин

myWebView.loadUrl("http://www.example.com")

Ява

myWebView.loadUrl("https://www.example.com");

Или загрузите URL из HTML-строки:

Котлин

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Ява

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

Ваше приложение должно иметь доступ к Интернету. Чтобы получить доступ к Интернету, запросите разрешение INTERNET в файле манифеста, как показано в следующем примере:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

Вы можете настроить свой WebView , выполнив любое из следующих действий:

  • Включение поддержки полноэкранного режима с помощью WebChromeClient . Этот класс также вызывается, когда WebView требуется разрешение на изменение пользовательского интерфейса хост-приложения, например, на создание или закрытие окон или отправку пользователю диалоговых окон JavaScript. Подробнее об отладке в этом контексте см. в статье «Отладка веб-приложений» .
  • Обработка событий, влияющих на отрисовку контента, таких как ошибки при отправке форм или навигации с помощью WebViewClient . Этот подкласс также можно использовать для перехвата загрузки URL.
  • Включение JavaScript путем изменения WebSettings .
  • Использование JavaScript для доступа к объектам фреймворка Android, внедренным в WebView .

Использовать JavaScript в WebView

Если веб-страница, которую вы хотите загрузить в WebView , использует JavaScript, необходимо включить JavaScript для WebView . После включения JavaScript вы сможете создавать интерфейсы между кодом вашего приложения и кодом JavaScript.

Включить JavaScript

JavaScript по умолчанию отключен в WebView . Вы можете включить его через WebSettings , прикреплённый к вашему WebView . Получите WebSettings с помощью getSettings() , а затем включите JavaScript с помощью setJavaScriptEnabled() .

См. следующий пример:

Котлин

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Ява

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings предоставляет доступ к множеству других настроек, которые могут оказаться вам полезными. Например, если вы разрабатываете веб-приложение, предназначенное специально для WebView в вашем приложении для Android, вы можете определить собственную строку пользовательского агента с помощью setUserAgentString() , а затем запросить пользовательского агента на вашей веб-странице, чтобы убедиться, что клиент, запрашивающий вашу веб-страницу, является вашим приложением для Android.

Привязать код JavaScript к коду Android

При разработке веб-приложения, специально предназначенного для WebView в вашем Android-приложении, вы можете создать интерфейсы между кодом JavaScript и клиентским кодом Android. Например, ваш код JavaScript может вызвать метод в коде Android для отображения Dialog вместо использования функции alert() JavaScript.

Чтобы связать новый интерфейс между вашим JavaScript и кодом Android, вызовите addJavascriptInterface() , передав ему экземпляр класса для привязки к вашему JavaScript и имя интерфейса, который ваш JavaScript может вызвать для доступа к классу.

Например, вы можете включить следующий класс в свое приложение для Android:

Котлин

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Ява

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

В этом примере класс WebAppInterface позволяет веб-странице создавать Toast -сообщение с помощью метода showToast() .

Вы можете привязать этот класс к JavaScript, который выполняется в вашем WebView , с помощью addJavascriptInterface() , как показано в следующем примере:

Котлин

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Ява

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

Это создаёт интерфейс Android для JavaScript, работающий в WebView . На этом этапе ваше веб-приложение получает доступ к классу WebAppInterface . Например, вот код HTML и JavaScript, который создаёт всплывающее сообщение с использованием нового интерфейса при нажатии пользователем кнопки:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

Инициализировать интерфейс Android из JavaScript не нужно. WebView автоматически делает его доступным на вашей веб-странице. Поэтому, когда пользователь нажимает кнопку, функция showAndroidToast() использует интерфейс Android для вызова метода WebAppInterface.showToast() .

Управление навигацией по страницам

Когда пользователь нажимает на ссылку на веб-странице в вашем WebView , Android по умолчанию запускает приложение, обрабатывающее URL-адреса. Обычно открывается веб-браузер по умолчанию и загружает целевой URL-адрес. Однако вы можете переопределить это поведение для вашего WebView , чтобы ссылки открывались в WebView . После этого вы можете разрешить пользователю перемещаться вперёд и назад по истории веб-страниц, хранящейся в вашем WebView .

Чтобы открывать ссылки, на которые нажимает пользователь, предоставьте WebViewClient для вашего WebView с помощью setWebViewClient() . Все ссылки, на которые нажимает пользователь, загружаются в ваш WebView . Если вам нужен больший контроль над тем, где загружается нажатая ссылка, создайте собственный WebViewClient , который переопределяет метод shouldOverrideUrlLoading() . В следующем примере предполагается, что MyWebViewClient — это внутренний класс Activity .

Котлин

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Ява

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

Затем создайте экземпляр этого нового WebViewClient для WebView :

Котлин

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Ява

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

Теперь, когда пользователь нажимает на ссылку, система вызывает метод shouldOverrideUrlLoading() , который проверяет, соответствует ли хост URL-адреса определённому домену, как определено в предыдущем примере. Если соответствует, метод возвращает false и не переопределяет загрузку URL-адреса. Он позволяет WebView загрузить URL-адрес как обычно. Если хост-адрес URL-адреса не совпадает, создаётся Intent для запуска Activity по умолчанию для обработки URL-адресов, который преобразуется в веб-браузер пользователя по умолчанию.

Обработка пользовательских URL-адресов

WebView применяет ограничения при запросе ресурсов и разрешении ссылок, использующих пользовательскую схему URL. Например, если вы реализуете обратные вызовы, такие как shouldOverrideUrlLoading() или shouldInterceptRequest() , то WebView будет вызывать их только для допустимых URL.

Например, WebView может не вызывать ваш метод shouldOverrideUrlLoading() для таких ссылок:

<a href="showProfile">Show Profile</a>

Недействительные URL-адреса, подобные показанному в предыдущем примере, обрабатываются в WebView некорректно, поэтому мы рекомендуем использовать корректный URL. Вы можете использовать пользовательскую схему или HTTPS-URL для домена, контролируемого вашей организацией.

Вместо использования простой строки в ссылке, как в предыдущем примере, вы можете использовать пользовательскую схему, например следующую:

<a href="example-app:showProfile">Show Profile</a>

Затем вы можете обработать этот URL в методе shouldOverrideUrlLoading() следующим образом:

Котлин

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Ява

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

API shouldOverrideUrlLoading() в первую очередь предназначен для запуска намерений для определённых URL-адресов. При его реализации обязательно возвращайте false для URL-адресов, обрабатываемых WebView . Однако вы не ограничены только запуском намерений. Вы можете заменить запуск намерений любым другим пользовательским поведением из предыдущих примеров кода.

Когда ваш WebView переопределяет загрузку URL, он автоматически накапливает историю посещённых веб-страниц. Вы можете перемещаться по истории вперёд и назад с помощью goBack() и goForward() .

Например, ниже показано, как ваша Activity может использовать кнопку «Назад» устройства для перехода назад:

Котлин

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Ява

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

Если ваше приложение использует AndroidX AppCompat 1.6.0+, вы можете упростить предыдущий фрагмент еще больше:

Котлин

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Ява

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

Метод canGoBack() возвращает true, если есть история посещений веб-страниц пользователем. Аналогично, метод canGoForward() можно использовать для проверки наличия истории пересылок. Если эта проверка не выполняется, то после того, как пользователь достигнет конца истории, goBack() и goForward() не будут выполнять никаких действий.

Обработка изменений конфигурации устройства

Во время выполнения состояние активности изменяется при изменении конфигурации устройства, например, когда пользователь поворачивает устройство или закрывает редактор методов ввода (IME). Эти изменения приводят к уничтожению активности объекта WebView и созданию новой активности, которая также создаёт новый объект WebView , загружающий URL уничтоженного объекта. Чтобы изменить поведение активности по умолчанию, можно изменить способ обработки изменений orientation в манифесте. Подробнее об обработке изменений конфигурации во время выполнения см. в статье Обработка изменений конфигурации .

Управление окнами

По умолчанию запросы на открытие новых окон игнорируются. Это справедливо как для JavaScript, так и для атрибута target в ссылке. Вы можете настроить WebChromeClient , чтобы задать собственное поведение при открытии нескольких окон.

Чтобы повысить безопасность приложения, рекомендуется запретить открытие всплывающих окон и новых окон. Самый безопасный способ реализовать это поведение — передать значение "true" в setSupportMultipleWindows() , но не переопределять метод onCreateWindow() , от которого зависит setSupportMultipleWindows() . Эта логика предотвращает загрузку любой страницы, ссылки на которую содержат target="_blank" .