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

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

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

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

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

<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" в своих ссылках.