تحميل محتوى داخل التطبيق

يمكنك توفير محتوى مستند إلى الويب، مثل 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"، انقر بزر الماوس الأيمن على المجلد التطبيق > src > الرئيسي، ثم اختَر جديد > الدليل.
    صورة تعرض قوائم إنشاء الدليل في &quot;استوديو Android&quot;
    الشكل 1. أنشئ مجلد مواد عرض لمشروعك.
  2. أدخِل اسمًا للمجلد "مواد العرض".
    صورة تعرض مجلد مواد العرض
    الشكل 2. أدخِل اسمًا لمجلد مواد العرض.
  3. انقر بزر الماوس الأيمن على مجلد assets، ثم انقر على New (جديد) > ملف. أدخِل 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 لمشروعك على محرك الأقراص الثابتة.

يوضّح الشكل 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 به. يجب أن يكون هذا عنوان URL HTTP(S).
  • data: هذا هو محتوى HTML الذي تريد عرضه كسلسلة.
  • mimeType: يجب ضبط هذه السمة عادةً على text/html.
  • encoding: لا يتم استخدام هذه السمة عندما يكون baseUrl عنوان URL يستخدم بروتوكول HTTP(S)، وبالتالي يمكن ضبطه على null.
  • historyUrl: تم ضبط هذه السياسة على قيمة baseUrl نفسها.

ننصح بشدة باستخدام عنوان URL يستخدم بروتوكول HTTP(S) على أنّه baseUrl، لأنّ ذلك يساعد في ضمان التزام تطبيقك بسياسة المصدر نفسه.

إذا لم تتمكّن من العثور على سمة baseUrl مناسبة للمحتوى الخاص بك وكنت تفضّل استخدام loadData()، عليك ترميز المحتوى باستخدام ترميز بنسبة مئوية أو ترميز Base64. ننصح بشدة باختيار ترميز 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:// وdata: مصادر مبهمة، ما يعني أنّه لا يمكنها الاستفادة من واجهات برمجة تطبيقات الويب الفعّالة، مثل fetch() أو XMLHttpRequest. تستخدم loadData() داخليًا عناوين URL لـ data:، لذا ننصح باستخدام WebViewAssetLoader أو loadDataWithBaseURL() بدلاً منها.
  • على الرغم من أنّ الترميزَين WebSettings.setAllowFileAccessFromFileURLs() وWebSettings.setAllowUniversalAccessFromFileURLs() يمكنهما التغلب على المشاكل المتعلّقة بعناوين URL التي تتضمّن file://، ننصحك بعدم ضبطهما على true لأنّ ذلك يؤدي إلى تعرُّض تطبيقك لعمليات الاستغلال المستندة إلى الملفات. وننصحك بضبطها بشكل صريح على false في جميع مستويات واجهة برمجة التطبيقات للحصول على أعلى مستوى من الأمان.
  • للأسباب نفسها، ننصح بعدم استخدام عنوانَي URL file://android_assets/ وfile://android_res/. ومن المفترض أن تكون الفئتان AssetsHandler وResourcesHandler بديلتَين.
  • تجنَّب استخدام MIXED_CONTENT_ALWAYS_ALLOW. هذا الإعداد ليس ضروريًا بشكل عام ويضعف أمان تطبيقك. ننصحك بتحميل المحتوى داخل التطبيق باستخدام النظام نفسه، HTTP أو HTTPS، الذي يعتمده موارد موقعك الإلكتروني، واستخدام MIXED_CONTENT_COMPATIBILITY_MODE أو MIXED_CONTENT_NEVER_ALLOW حسب الاقتضاء.