بار کردن محتوای درون‌برنامه

می‌توانید محتوای مبتنی بر وب (مانند HTML، جاوا اسکریپت، و CSS) را برای استفاده برنامه‌تان ارائه دهید که به‌جای واکشی ازطریق اینترنت، آن را به‌صورت ایستا در برنامه کامپایل می‌کنید.

محتوای درون‌برنامه به دسترسی به اینترنت نیاز ندارد یا پهنای باند کاربر را مصرف نمی‌کند. اگر محتوا به‌طور خاص فقط برای WebView طراحی شده باشد—یعنی به برقراری ارتباط با برنامه به زبان اصلی دستگاه وابسته باشد—کاربران نمی‌توانند به‌طور تصادفی آن را در مرورگر وب بار کنند.

بااین‌حال، محتوای درون‌برنامه معایبی هم دارد. به‌روزرسانی محتوای مبتنی بر وب مستلزم ارسال به‌روزرسانی جدید برنامه است و اگر کاربران نسخه‌های قدیمی برنامه را داشته باشند، احتمال عدم تطابق محتوای موجود در وب‌سایت با محتوای موجود در برنامه دستگاه شما وجود دارد.

WebViewAssetLoader

WebViewAssetLoader روشی انعطاف‌پذیر و کارآمد برای بار کردن محتوای درون‌برنامه در شیء WebView است. این کلاس از موارد زیر پشتیبانی می‌کند:

  • بار کردن محتوا با نشانی وب HTTP(S) برای سازگاری با خط‌مشی هم‌مبدأ.
  • بار کردن زیرمنابعی مثل جاوا اسکریپت، CSS، تصاویر، و iframe.

WebViewAssetLoader را در فایل فعالیت اصلی‌تان بگنجانید. در زیر نمونه‌ای از بار کردن محتوای وب ساده از پوشه دارایی‌ها آورده شده است:

کاتلین

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

جاوا

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»، روی پوشه app > src > main کلیک راست کنید و سپس جدید > دایرکتوری را انتخاب کنید.
    تصویری که منوهای ایجاد دایرکتوری «استودیو Android» را نشان می‌دهد
    شکل ۱. پوشه دارایی‌ها را برای پروژه‌تان ایجاد کنید.
  2. نام پوشه را «assets» بگذارید.
    تصویری که پوشه دارایی را نشان می‌دهد
    شکل ۲. پوشه دارایی‌ها را نام‌گذاری کنید.
  3. روی پوشه assets کلیک راست کنید و سپس روی New (جدید) > File (فایل) کلیک کنید. index.html را وارد کنید و کلید Return یا Enter را فشار دهید.
  4. مرحله قبلی را تکرار کنید تا فایل خالی برای stylesheet.css ایجاد شود.
  5. فایل‌های خالی‌ای را که ایجاد کرده‌اید با محتوای دو نمونه کد بعدی پر کنید.
```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 پروژه‌تان در دیسک سخت منتقل کنید.

شکل ۴ تصویری را که اضافه کردید و نوشتار نمونه کدهای قبلی را نشان می‌دهد که در برنامه ارائه شده است.

تصویری که برونداد پرداز‌شده برنامه را نشان می‌دهد
شکل ۴. فایل HTML درون‌برنامه و فایل تصویری ارائه‌شده در برنامه.

برای تکمیل برنامه، کارهای زیر را انجام دهید:

  1. با افزودن کد زیر به روش onCreate()، گرداننده‌ها را ثبت کنید و AssetLoader را پیکربندی کنید:

    کاتلین

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

    جاوا

    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()، محتوا را بار کنید:

    کاتلین

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

    جاوا

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

محتوای درون‌برنامه را با منابع وب‌سایتتان ترکیب کنید

ممکن است برنامه‌تان نیاز داشته باشد ترکیبی از محتوای درون‌برنامه و محتوای اینترنت را بار کند، مثل صفحه HTML درون‌برنامه که با CSS وب‌سایتتان سبک‌بندی شده است. WebViewAssetLoader از این مورد استفاده پشتیبانی می‌کند. اگر هیچ‌کدام از نمونه‌های ثبت‌شده PathHandler نتوانند منبعی برای مسیر ارائه‌شده پیدا کنند، WebView به بار کردن محتوا از اینترنت برمی‌گردد. اگر محتوای درون‌برنامه را با منابع وب‌سایتتان ترکیب می‌کنید، مسیرهای دایرکتوری، مثل /assets/ یا /resources/ را برای منابع درون‌برنامه رزرو کنید. از ذخیره کردن منابع وب‌سایتتان در این مکان‌ها خودداری کنید.

کاتلین

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)

جاوا

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

برای دیدن نمونه‌ای از صفحه HTML درون‌برنامه که داده‌های JSON میزبانی‌شده در وب را واکشی می‌کند، WebView نسخه نمایشی را در GitHub ببینید.

loadDataWithBaseURL

وقتی برنامه‌تان فقط باید صفحه HTML را بار کند و نیازی به رهگیری زیرمنابع ندارد، از loadDataWithBaseURL() استفاده کنید که به دارایی‌های برنامه نیاز ندارد. می‌توانید از آن همان‌طور که در نمونه کد زیر نشان داده شده است استفاده کنید:

کاتلین

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

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

جاوا

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

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

مقادیر آرگومان را بادقت انتخاب کنید. موارد زیر را درنظر بگیرید:

  • ‫baseUrl: این نشانی وب است که محتوای HTML شما به‌عنوان آن بار می‌شود. این باید نشانی وب HTTP(S) باشد.
  • ‫data: این محتوای HTML است که می‌خواهید به‌صورت رشته نمایش دهید.
  • mimeType: این مقدار معمولاً باید روی text/html تنظیم شود.
  • encoding: وقتی baseUrl نشانی وب HTTP(S) باشد، این مورد استفاده نمی‌شود، بنابراین می‌توان آن را روی null تنظیم کرد.
  • historyUrl: این مقدار با مقدار baseUrl یکسان است.

قویاً توصیه می‌کنیم از نشانی وب HTTP(S) به‌عنوان baseUrl استفاده کنید، زیرا این کار کمک می‌کند مطمئن شوید برنامه‌تان از خط‌مشی هم‌مبدأ تبعیت می‌کند.

اگر نمی‌توانید baseUrl مناسبی برای محتوایتان پیدا کنید و ترجیح می‌دهید از loadData() استفاده کنید، باید محتوا را با کدبندی درصدی یا کدبندی Base64 کدبندی کنید. اکیداً توصیه می‌کنیم کدگذاری Base64 را انتخاب کنید و از میاناهای برنامه‌سازی کاربردی Android برای کدگذاری این به‌صورت برنامه‌نویسی‌شده استفاده کنید، همان‌طور که در نمونه کد زیر نشان داده شده است:

کاتلین

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

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

جاوا

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

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

مواردی که باید از آن‌ها اجتناب کنید

چندین روش دیگر برای بار کردن محتوای درون‌برنامه وجود دارد، اما اکیداً توصیه می‌کنیم از آن‌ها استفاده نکنید:

  • نشانی‌های وب file:// و نشانی‌های وب data: به‌عنوان مبدأهای مبهم درنظر گرفته می‌شوند، یعنی نمی‌توانند از میاناهای برنامه‌سازی کاربردی وب قدرتمند مانند fetch() یا XMLHttpRequest استفاده کنند. loadData() به‌طور داخلی از نشانی‌های وب data: استفاده می‌کند، بنابراین توصیه می‌کنیم به‌جای آن از WebViewAssetLoader یا loadDataWithBaseURL() استفاده کنید.
  • اگرچه WebSettings.setAllowFileAccessFromFileURLs() و WebSettings.setAllowUniversalAccessFromFileURLs() می‌توانند مشکلات مربوط به نشانی‌های وب file:// را حل کنند، توصیه می‌کنیم این موارد را روی true تنظیم نکنید زیرا انجام این کار برنامه‌تان را دربرابر سوءاستفاده‌های مبتنی بر فایل آسیب‌پذیر می‌کند. توصیه می‌کنیم برای داشتن قوی‌ترین امنیت، این موارد را به‌طور صریح روی false در همه سطوح API تنظیم کنید.
  • به همین دلایل، توصیه می‌کنیم از نشانی‌های وب file://android_assets/ و file://android_res/ استفاده نکنید. کلاس‌های AssetsHandler و ResourcesHandler به‌عنوان جایگزین‌های مستقیم درنظر گرفته شده‌اند.
  • از MIXED_CONTENT_ALWAYS_ALLOW استفاده نکنید. این تنظیم معمولاً ضروری نیست و امنیت برنامه‌تان را تضعیف می‌کند. توصیه می‌کنیم محتوای درون‌برنامه‌ای‌تان را ازطریق همان طرحی بار کنید که منابع وب‌سایتتان بار می‌شوند (HTTP یا HTTPS) و از MIXED_CONTENT_COMPATIBILITY_MODE یا MIXED_CONTENT_NEVER_ALLOW به‌طور مناسب استفاده کنید.