يمكنك توفير محتوى مستند إلى الويب، مثل 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 إلى مواد العرض.
- تنتمي الصور والملفات الثنائية الأخرى إلى الموارد.
لإضافة ملفات ويب نصية إلى مشروع، عليك إجراء ما يلي:
- في "استوديو Android"، انقر بزر الماوس الأيمن على المجلد التطبيق > src > الرئيسي، ثم اختَر جديد > الدليل.
- أدخِل اسمًا للمجلد "مواد العرض".
- انقر بزر الماوس الأيمن على مجلد assets، ثم انقر على New (جديد) > ملف.
أدخِل
index.html
واضغط على المفتاح Return أو Enter. - كرِّر الخطوة السابقة لإنشاء ملف فارغ لـ
stylesheet.css
. - املأ الملفات الفارغة التي أنشأتها باستخدام المحتوى في نموذجَي الرمز التاليَين.
```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;
}
```
لإضافة ملف ويب مستند إلى صورة إلى مشروعك، قم بما يلي:
نزِّل الملف
Android_symbol_green_RGB.png
على جهازك المحلي.أعد تسمية الملف إلى
android_robot.png
.انقِل الملف يدويًا إلى دليل
main/res/drawable
لمشروعك على محرك الأقراص الثابتة.
يوضّح الشكل 4 الصورة التي أضفتها والنص من نماذج التعليمات البرمجية السابقة التي تم عرضها في أحد التطبيقات.
لإكمال التطبيق، قم بما يلي:
سجِّل المعالجات واضبط
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));
حمِّل المحتوى من خلال إضافة الرمز التالي إلى طريقة
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
حسب الاقتضاء.