โหลดเนื้อหาในแอป

คุณสามารถให้เนื้อหาบนเว็บ เช่น HTML, JavaScript และ CSS - เพื่อให้แอปใช้งานได้ คุณจะทำการคอมไพล์ลงในแอปแบบคงที่แทน ในการเข้าถึงอินเทอร์เน็ต

เนื้อหาในแอปไม่จำเป็นต้องเชื่อมต่ออินเทอร์เน็ตหรือเปลืองแบนด์วิดท์ของผู้ใช้ ถ้า เนื้อหานี้ออกแบบมาสำหรับ WebView เท่านั้น กล่าวคือ ขึ้นอยู่กับการสื่อสารกับแอปที่มาพร้อมเครื่อง ผู้ใช้ โหลดในเว็บเบราว์เซอร์

แต่เนื้อหาในแอปมีข้อเสียบางอย่าง การอัปเดตเนื้อหาบนเว็บ ต้องมีการจัดส่งอัปเดตแอปใหม่ และเป็นไปได้ว่าข้อมูลไม่ตรงกัน เนื้อหาระหว่างสิ่งที่อยู่บนเว็บไซต์กับสิ่งที่อยู่ในแอปพลิเคชันบนอุปกรณ์ของคุณ ผู้ใช้มีแอปเวอร์ชันเก่า

WebViewAssetLoader

WebViewAssetLoader เป็น วิธีการที่ยืดหยุ่นและมีประสิทธิภาพในการโหลดเนื้อหาในแอป ออบเจ็กต์ WebView คลาสนี้สนับสนุน ดังต่อไปนี้:

  • การโหลดเนื้อหาด้วย URL ของ HTTP(S) เพื่อให้เข้ากันได้กับต้นทางเดียวกัน
  • กำลังโหลดทรัพยากรย่อย เช่น JavaScript, CSS, รูปภาพ และ iframe

รวม WebViewAssetLoader ในไฟล์กิจกรรมหลัก ต่อไปนี้เป็น ตัวอย่างการโหลดเนื้อหาเว็บแบบง่ายจากโฟลเดอร์เนื้อหา

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));
    }
}

แอปของคุณต้องกำหนดค่าอินสแตนซ์ WebViewAssetLoader ให้เหมาะกับความต้องการ ส่วนถัดไปจะมีตัวอย่าง

สร้างทรัพยากรและชิ้นงานในแอป

WebViewAssetLoader อาศัย PathHandler อินสแตนซ์เพื่อโหลดทรัพยากรที่สอดคล้องกับเส้นทางทรัพยากรที่ระบุ แม้ว่าคุณ สามารถใช้อินเทอร์เฟซนี้เพื่อดึงข้อมูลทรัพยากรตามที่แอปของคุณต้องการ แพ็กเกจคลัง Webkit AssetsPathHandler และ ResourcesPathHandler สำหรับการโหลดเนื้อหาและทรัพยากรของ Android ตามลำดับ

ในการเริ่มต้นใช้งาน ให้สร้างชิ้นงานและทรัพยากรสำหรับแอป โดยทั่วไป การตั้งค่า ดังต่อไปนี้

  • ไฟล์ข้อความ เช่น HTML, JavaScript และ CSS จะอยู่ในเนื้อหา
  • รูปภาพและไฟล์ไบนารีอื่นๆ จะอยู่ในทรัพยากร

หากต้องการเพิ่มไฟล์เว็บแบบข้อความลงในโครงการ ให้ทำตามขั้นตอนต่อไปนี้

  1. ใน Android Studio ให้คลิกขวาที่แอป > src > โฟลเดอร์หลัก จากนั้นเลือกใหม่ > ไดเรกทอรี
    วันที่ รูปภาพแสดงเมนูไดเรกทอรีการสร้างของ Android Studio
    รูปที่ 1 สร้างโฟลเดอร์เนื้อหาสำหรับ
  2. ตั้งชื่อโฟลเดอร์ว่า "assets"
    วันที่ รูปภาพแสดงโฟลเดอร์เนื้อหา
    รูปที่ 2 ตั้งชื่อโฟลเดอร์เนื้อหา
  3. คลิกขวาที่โฟลเดอร์ assets แล้วคลิกใหม่ > ไฟล์ ป้อน index.html และกด Return หรือ ปุ่ม Enter
  4. ทำซ้ำขั้นตอนก่อนหน้าเพื่อสร้างไฟล์ว่างสำหรับ stylesheet.css
  5. ใส่ไฟล์เปล่าที่คุณสร้างด้วยเนื้อหาในโค้ด 2 โค้ดถัดไป ตัวอย่าง
```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;
}
```

หากต้องการเพิ่มไฟล์เว็บที่เป็นรูปภาพลงในโครงการ ให้ทำตามขั้นตอนต่อไปนี้

  1. ดาวน์โหลด Android_symbol_green_RGB.png ไปยังเครื่องภายในของคุณ

  2. เปลี่ยนชื่อไฟล์เป็น android_robot.png

  3. ย้ายไฟล์ไปยังไดเรกทอรี main/res/drawable ของโปรเจ็กต์ด้วยตนเองใน ฮาร์ดไดรฟ์ของคุณ

รูปที่ 4 แสดงรูปภาพที่คุณเพิ่มและข้อความจากตัวอย่างโค้ดก่อนหน้า ที่แสดงในแอป

วันที่ รูปภาพแสดงเอาต์พุตที่แอปแสดงผล
รูปที่ 4 ไฟล์ HTML และไฟล์ภาพในแอป ที่แสดงในแอป

ในการทำให้แอปเสร็จสมบูรณ์ ให้ทำตามขั้นตอนต่อไปนี้

  1. ลงทะเบียนเครื่องจัดการและกำหนดค่า AssetLoader ด้วยการเพิ่ม โค้ดต่อไปนี้ไปยังเมธอด 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. โหลดเนื้อหาโดยการเพิ่มโค้ดต่อไปนี้ลงในเมธอด onCreate()

    Kotlin

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

    Java

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

เพิ่มเนื้อหาในแอปเข้ากับแหล่งข้อมูลจากเว็บไซต์ของคุณ

แอปของคุณอาจต้องโหลดเนื้อหาในแอปและเนื้อหาจาก เช่น หน้า HTML ในแอปที่จัดรูปแบบโดย CSS ของเว็บไซต์ WebViewAssetLoader รองรับกรณีการใช้งานนี้ หากไม่มีโดเมนที่จดทะเบียน อินสแตนซ์ PathHandler รายการค้นหาทรัพยากรสำหรับเส้นทางที่ระบุได้ WebView จะตก กลับไปโหลดเนื้อหาจากอินเทอร์เน็ต หากคุณผสมเนื้อหาในแอปกับ ทรัพยากรจากเว็บไซต์ จองเส้นทางไดเรกทอรี เช่น /assets/ หรือ /resources/ สำหรับทรัพยากรในแอป หลีกเลี่ยงการจัดเก็บทรัพยากรใดๆ จาก เว็บไซต์ในประเทศเหล่านั้น

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);

ดูการสาธิต WebView เกี่ยวกับ GitHub เพื่อดูตัวอย่างหน้า HTML ในแอปที่ดึงข้อมูล JSON ที่โฮสต์บนเว็บ

loadDataWithBaseURL

เมื่อแอปของคุณต้องการโหลดหน้า HTML เท่านั้นและไม่จําเป็นต้องสกัดกั้น ทรัพยากรย่อย ให้พิจารณาใช้ loadDataWithBaseURL() ซึ่งไม่ต้องใช้ชิ้นงานแอป คุณสามารถใช้งานบัญชีได้ดังที่แสดงในรหัสต่อไปนี้ ตัวอย่าง:

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);

เลือกค่าอาร์กิวเมนต์อย่างรอบคอบ ลองพิจารณาสิ่งเหล่านี้

  • baseUrl: นี่คือ URL ที่โหลดเนื้อหา HTML ของคุณ ค่านี้ต้องเป็น HTTP(S) URL
  • data: นี่คือเนื้อหา HTML ที่คุณต้องการแสดงเป็นสตริง
  • mimeType: โดยปกติต้องตั้งค่าเป็น text/html
  • encoding: ไม่มีการใช้เมื่อ baseUrl เป็น URL แบบ HTTP(S) ดังนั้นจึงอาจ ตั้งค่าเป็น null
  • historyUrl: ตั้งค่าเป็นค่าเดียวกับ baseUrl

เราขอแนะนำให้ใช้ URL แบบ HTTP(S) เป็น baseUrl เพราะจะช่วยได้ ตรวจสอบว่าแอปเป็นไปตามนโยบายต้นทางเดียวกัน

หากไม่พบ baseUrl ที่เหมาะกับเนื้อหาและต้องการใช้ loadData(), คุณต้องเข้ารหัสเนื้อหาด้วย การเข้ารหัสเปอร์เซ็นต์ หรือ พื้นฐาน 64 การเข้ารหัส เราขอแนะนำอย่างยิ่งให้เลือกการเข้ารหัส Base64 และใช้ Android API ในการเข้ารหัส นี้โดยอัตโนมัติ ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้

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");

สิ่งที่ควรหลีกเลี่ยง

มีวิธีอื่นๆ อีกหลายวิธีในการโหลดเนื้อหาในแอป แต่เราขอแนะนำอย่างยิ่ง เทียบกับคำนั้น:

  • URL file:// รายการและ URL data: รายการถือเป็นต้นทางที่ไม่ชัดเจน ซึ่งหมายความว่าพวกเขาก็จะไม่สามารถใช้ประโยชน์จาก Web API ที่มีประสิทธิภาพอย่างเช่น fetch() หรือ XMLHttpRequest loadData() ใช้ URL data: รายการเป็นการภายใน เราจึงแนะนำให้ใช้ WebViewAssetLoader หรือ loadDataWithBaseURL() แทน
  • แม้ว่า WebSettings.setAllowFileAccessFromFileURLs() และ WebSettings.setAllowUniversalAccessFromFileURLs() ช่วยแก้ปัญหาเกี่ยวกับ URL ของ file:// ได้ เราขอแนะนำให้ไม่ต้องตั้งค่า ไปยัง true เนื่องจากจะทำให้แอปมีความเสี่ยงต่อไฟล์ การเจาะช่องโหว่ เราขอแนะนำให้ตั้งค่าเหล่านี้เป็น false อย่างชัดเจนใน API ทุกระดับ เพื่อการรักษาความปลอดภัยที่เข้มงวดที่สุด
  • ด้วยเหตุผลเดียวกันนี้ เราขอแนะนำให้ใช้ file://android_assets/ และ URL file://android_res/ รายการ AssetsHandler และ ResourcesHandler ชั้นเรียนจะใช้แทนแบบดร็อปอิน
  • หลีกเลี่ยงการใช้ MIXED_CONTENT_ALWAYS_ALLOW การตั้งค่านี้มักไม่ใช่สิ่งจำเป็นและทำให้ความปลอดภัยของแอปอ่อนแอลง เราขอแนะนำให้โหลดเนื้อหาในแอปผ่านรูปแบบเดียวกัน ไม่ว่าจะเป็น HTTP หรือ HTTPS เป็นทรัพยากรของเว็บไซต์และการใช้ MIXED_CONTENT_COMPATIBILITY_MODE หรือ MIXED_CONTENT_NEVER_ALLOW, ตามความเหมาะสม