Memuat konten dalam aplikasi

Anda dapat menyediakan konten berbasis web—seperti HTML, JavaScript, dan CSS—untuk digunakan aplikasi Anda yang dikompilasi secara statis ke dalam aplikasi, bukan diambil melalui internet.

Konten dalam aplikasi tidak memerlukan akses internet atau menghabiskan bandwidth pengguna. Jika konten didesain khusus untuk WebView saja—yaitu, bergantung pada komunikasi dengan aplikasi native—pengguna tidak akan dapat memuatnya secara tidak sengaja di browser web.

Namun, ada beberapa kelemahan dari konten dalam aplikasi. Mengupdate konten berbasis web memerlukan pengiriman update aplikasi baru, dan ada kemungkinan konten yang tidak cocok antara konten yang ada di situs dan yang ada di aplikasi di perangkat Anda jika pengguna memiliki versi aplikasi yang sudah tidak berlaku.

WebViewAssetLoader

WebViewAssetLoader adalah cara yang fleksibel dan berperforma tinggi untuk memuat konten dalam aplikasi di objek WebView. Class ini mendukung hal berikut:

  • Memuat konten dengan URL HTTP(S) untuk kompatibilitas dengan kebijakan origin yang sama.
  • Memuat subresource seperti JavaScript, CSS, gambar, dan iframe.

Sertakan WebViewAssetLoader dalam file aktivitas utama Anda. Berikut adalah contoh pemuatan konten web sederhana dari folder aset:

Kotlin

private class LocalContentWebViewClient(private val assetLoader: WebViewAssetLoader) : WebViewClientCompat() {
    @RequiresApi(21)
    override fun shouldInterceptRequest(
        view: WebView,
        request: WebResourceRequest
    ): WebResourceResponse? {
        return assetLoader.shouldInterceptRequest(request.url)
    }

    // To support API < 21.
    override fun shouldInterceptRequest(
        view: WebView,
        url: String
    ): WebResourceResponse? {
        return assetLoader.shouldInterceptRequest(Uri.parse(url))
    }
}

Java

private static class LocalContentWebViewClient extends WebViewClientCompat {

    private final WebViewAssetLoader mAssetLoader;

    LocalContentWebViewClient(WebViewAssetLoader assetLoader) {
        mAssetLoader = assetLoader;
    }

    @Override
    @RequiresApi(21)
    public WebResourceResponse shouldInterceptRequest(WebView view,
                                     WebResourceRequest request) {
        return mAssetLoader.shouldInterceptRequest(request.getUrl());
    }

    @Override
    @SuppressWarnings("deprecation") // To support API < 21.
    public WebResourceResponse shouldInterceptRequest(WebView view,
                                     String url) {
        return mAssetLoader.shouldInterceptRequest(Uri.parse(url));
    }
}

Aplikasi Anda harus mengonfigurasi instance WebViewAssetLoader agar sesuai dengan kebutuhannya. Bagian berikutnya memiliki contoh.

Membuat aset dan resource dalam aplikasi

WebViewAssetLoader bergantung pada instance PathHandler untuk memuat resource yang sesuai dengan jalur resource tertentu. Walaupun Anda dapat menerapkan antarmuka ini untuk mengambil resource sesuai kebutuhan aplikasi, library Webkit memaketkan AssetsPathHandler dan ResourcesPathHandler untuk memuat aset dan resource Android.

Untuk memulai, buat aset dan resource untuk aplikasi Anda. Secara umum, hal-hal berikut berlaku:

  • File teks seperti HTML, JavaScript, dan CSS termasuk dalam aset.
  • Gambar dan file biner lainnya berada dalam resource.

Untuk menambahkan file web berbasis teks ke sebuah project, lakukan hal berikut:

  1. Di Android Studio, klik kanan folder app > src > main, lalu pilih New > Directory.
    Gambar yang menunjukkan menu buat direktori Android Studio
    Gambar 1. Buat folder aset untuk project Anda.
  2. Beri nama folder "assets".
    Gambar yang menampilkan folder aset
    Gambar 2. Beri nama folder aset.
  3. Klik kanan folder assets, lalu klik New > File. Masukkan index.html, lalu tekan tombol Return atau Enter.
  4. Ulangi langkah sebelumnya untuk membuat file kosong bagi stylesheet.css.
  5. Isi file kosong yang Anda buat dengan konten di dua contoh kode berikutnya.
```html
<!-- index.html content -->

<html>
  <head>
    <!-- Tip: Use relative URLs when referring to other in-app content to give
              your app code the flexibility to change the scheme or domain as
              necessary. -->
    <link rel="stylesheet" href="/assets/stylesheet.css">
  </head>
  <body>
    <p>This file is loaded from in-app content.</p>
    <p><img src="/res/drawable/android_robot.png" alt="Android robot" width="100"></p>
  </body>
</html>
```

```css
<!-- stylesheet.css content -->

body {
  background-color: lightblue;
}
```

Untuk menambahkan file web berbasis gambar ke project Anda, lakukan hal berikut:

  1. Download file Android_symbol_green_RGB.png ke komputer lokal Anda.

  2. Ganti nama file menjadi android_robot.png.

  3. Pindahkan file secara manual ke direktori main/res/drawable project di hard drive Anda.

Gambar 4 menunjukkan gambar yang Anda tambahkan dan teks dari contoh kode sebelumnya yang dirender dalam aplikasi.

Gambar yang menunjukkan output yang dirender oleh aplikasi
Gambar 4. File HTML dan file gambar dalam aplikasi yang dirender di aplikasi.

Untuk menyelesaikan aplikasi, lakukan hal berikut:

  1. Daftarkan pengendali dan konfigurasikan AssetLoader dengan menambahkan kode berikut ke metode onCreate():

    Kotlin

    val assetLoader = WebViewAssetLoader.Builder()
                           .addPathHandler("/assets/", AssetsPathHandler(this))
                           .addPathHandler("/res/", ResourcesPathHandler(this))
                           .build()
    webView.webViewClient = LocalContentWebViewClient(assetLoader)
    

    Java

    final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
             .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
             .addPathHandler("/res/", new WebViewAssetLoader.ResourcesPathHandler(this))
             .build();
    mWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));
    
  2. Muat konten dengan menambahkan kode berikut ke metode onCreate():

    Kotlin

    webView.loadUrl("https://appassets.androidplatform.net/assets/index.html")
    

    Java

    mWebView.loadUrl("https://appassets.androidplatform.net/assets/index.html");
    

Menggabungkan konten dalam aplikasi dengan referensi dari situs Anda

Aplikasi Anda mungkin perlu memuat campuran konten dalam aplikasi dan konten dari internet, seperti halaman HTML dalam aplikasi yang diberi gaya oleh CSS situs Anda. WebViewAssetLoader mendukung kasus penggunaan ini. Jika tidak ada instance PathHandler terdaftar yang dapat menemukan resource untuk jalur tertentu, WebView akan kembali memuat konten dari internet. Jika Anda menggabungkan konten dalam aplikasi dengan resource dari situs Anda, cadangkan jalur direktori, seperti /assets/ atau /resources/, untuk resource dalam aplikasi. Hindari menyimpan resource apa pun dari situs Anda di lokasi tersebut.

Kotlin

val assetLoader = WebViewAssetLoader.Builder()
                        .setDomain("example.com") // Replace this with your website's domain.
                        .addPathHandler("/assets/", AssetsPathHandler(this))
                        .build()

webView.webViewClient = LocalContentWebViewClient(assetLoader)
val inAppHtmlUrl = "https://example.com/assets/index.html"
webView.loadUrl(inAppHtmlUrl)
val websiteUrl = "https://example.com/website/data.json"

// JavaScript code to fetch() content from the same origin.
val jsCode = "fetch('$websiteUrl')" +
        ".then(resp => resp.json())" +
        ".then(data => console.log(data));"

webView.evaluateJavascript(jsCode, null)

Java

final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
           .setDomain("example.com") // Replace this with your website's domain.
           .addPathHandler("/assets/", new AssetsPathHandler(this))
           .build();

mWebView.setWebViewClient(new LocalContentWebViewClient(assetLoader));
String inAppHtmlUrl = "https://example.com/assets/index.html";
mWebView.loadUrl(inAppHtmlUrl);
String websiteUrl = "https://example.com/website/data.json";

// JavaScript code to fetch() content from the same origin.
String jsCode = "fetch('" + websiteUrl + "')" +
      ".then(resp => resp.json())" +
      ".then(data => console.log(data));";

mWebView.evaluateJavascript(jsCode, null);

Lihat demo WebView di GitHub untuk mengetahui contoh halaman HTML dalam aplikasi yang mengambil data JSON yang dihosting web.

loadDataWithBaseURL

Jika aplikasi Anda hanya perlu memuat halaman HTML dan tidak perlu mencegat subresource, sebaiknya gunakan loadDataWithBaseURL(), yang tidak memerlukan aset aplikasi. Anda dapat menggunakannya seperti yang ditunjukkan pada contoh kode berikut:

Kotlin

val html = "<html><body><p>Hello world</p></body></html>"
val baseUrl = "https://example.com/"

webView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl)

Java

String html = "<html><body><p>Hello world</p></body></html>";
String baseUrl = "https://example.com/";

mWebView.loadDataWithBaseURL(baseUrl, html, "text/html", null, baseUrl);

Pilih nilai argumen dengan hati-hati. Pertimbangkan hal berikut:

  • baseUrl: ini adalah URL tempat konten HTML Anda dimuat. Ini harus berupa URL HTTP(S).
  • data: ini adalah konten HTML yang ingin Anda tampilkan sebagai string.
  • mimeType: ini biasanya harus ditetapkan ke text/html.
  • encoding: tidak digunakan jika baseUrl adalah URL HTTP(S), sehingga dapat ditetapkan ke null.
  • historyUrl: ini ditetapkan ke nilai yang sama dengan baseUrl.

Sebaiknya gunakan URL HTTP(S) sebagai baseUrl karena hal ini membantu memastikan aplikasi Anda mematuhi kebijakan origin yang sama.

Jika tidak dapat menemukan baseUrl yang sesuai untuk konten Anda dan lebih memilih untuk menggunakan loadData(), Anda harus mengenkode konten dengan encoding persen atau encoding Base64. Sebaiknya pilih encoding Base64 dan gunakan Android API untuk mengenkode hal ini secara terprogram, seperti ditunjukkan dalam contoh kode berikut:

Kotlin

val encodedHtml: String = Base64.encodeToString(html.toByteArray(), Base64.NO_PADDING)

webView.loadData(encodedHtml, mimeType, "base64")

Java

String encodedHtml = Base64.encodeToString(html.getBytes(), Base64.NO_PADDING);

mWebView.loadData(encodedHtml, mimeType, "base64");

Hal yang perlu dihindari

Ada beberapa cara lain untuk memuat konten dalam aplikasi, tetapi sebaiknya Anda tidak melakukannya:

  • URL file:// dan URL data: dianggap sebagai asal buram, yang berarti URL tersebut tidak dapat memanfaatkan API web yang canggih seperti fetch() atau XMLHttpRequest. loadData() secara internal menggunakan URL data:, jadi sebaiknya gunakan WebViewAssetLoader atau loadDataWithBaseURL() sebagai gantinya.
  • Meskipun WebSettings.setAllowFileAccessFromFileURLs() dan WebSettings.setAllowUniversalAccessFromFileURLs() dapat mengatasi masalah dengan URL file://, sebaiknya jangan menetapkannya ke true karena akan membuat aplikasi Anda rentan terhadap eksploitasi berbasis file. Sebaiknya tetapkan setelan ini secara eksplisit ke false pada semua API level untuk keamanan terkuat.
  • Untuk alasan yang sama, sebaiknya jangan gunakan URL file://android_assets/ dan file://android_res/. Class AssetsHandler dan ResourcesHandler dimaksudkan untuk menjadi pengganti langsung.
  • Hindari penggunaan MIXED_CONTENT_ALWAYS_ALLOW. Setelan ini umumnya tidak diperlukan dan melemahkan keamanan aplikasi Anda. Sebaiknya muat konten dalam aplikasi Anda menggunakan skema yang sama—HTTP atau HTTPS—sebagai resource situs Anda dan gunakan MIXED_CONTENT_COMPATIBILITY_MODE atau MIXED_CONTENT_NEVER_ALLOW, sebagaimana diperlukan.