إنشاء تطبيقات ويب في WebView

استخدِم WebView لتقديم تطبيق ويب أو صفحة ويب كجزء من تطبيق عميل. الفئة WebView هي إضافة لفئة View من Android تسمح لك بعرض صفحات الويب كجزء من تنسيق نشاطك. وهو لا يتضمن ميزات متصفح الويب المطور بالكامل، مثل عناصر التحكم في التنقل أو شريط العناوين. كل ما يفعله WebView هو عرض صفحة ويب تلقائيًا.

يمكن أن يساعدك WebView في تقديم معلومات داخل تطبيقك قد تحتاج إلى تعديلها، مثل اتفاقية المستخدم النهائي أو دليل المستخدم. ضمن تطبيق Android، يمكنك إنشاء Activity يحتوي على WebView، ثم استخدامه لعرض المستند الذي تتم استضافته على الإنترنت.

يمكن أن تساعد ميزة WebView أيضًا عندما يوفّر تطبيقك بيانات للمستخدم تتطلّب اتصالاً بالإنترنت لاسترداد البيانات، مثل البريد الإلكتروني. في هذه الحالة، قد تجد أنه من الأسهل إنشاء WebView في تطبيق Android الذي يُظهر صفحة ويب تحتوي على جميع بيانات المستخدمين، بدلاً من تنفيذ طلب شبكة ثم تحليل البيانات وعرضها بتنسيق Android. يمكنك بدلاً من ذلك تصميم صفحة ويب مخصَّصة للأجهزة التي تعمل بنظام التشغيل Android، ثم استخدام WebView في تطبيق Android لتحميل صفحة الويب.

يشرح هذا المستند كيفية بدء استخدام WebView وكيفية ربط JavaScript من صفحة الويب بالرمز من جهة العميل في تطبيق Android وكيفية التعامل مع التنقّل في الصفحات وكيفية إدارة النوافذ عند استخدام WebView.

استخدام WebView على الإصدارات السابقة من Android

لاستخدام أحدث إمكانيات WebView بأمان على الجهاز الذي يعمل عليه تطبيقك، يمكنك إضافة مكتبة AndroidX Webkit. هذه مكتبة ثابتة يمكنك إضافتها إلى تطبيقك لاستخدام واجهات برمجة تطبيقات android.webkit غير المتاحة لإصدارات النظام الأساسي السابقة.

أضِفه إلى ملف build.gradle على النحو التالي:

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

رائع

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

اطّلع على المثال WebView على GitHub لمزيد من التفاصيل.

إضافة WebView إلى تطبيقك

لإضافة WebView إلى تطبيقك، يمكنك تضمين العنصر <WebView> في تنسيق نشاطك أو ضبط نافذة Activity بالكامل على أنّها WebView في onCreate().

إضافة WebView في تنسيق النشاط

لإضافة WebView إلى تطبيقك في التنسيق، أضِف الرمز التالي إلى ملف XML لتنسيق نشاطك:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

لتحميل صفحة ويب في WebView، استخدِم السمة loadUrl()، كما هو موضّح في المثال التالي:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

إضافة WebView في onCreate()

لإضافة WebView إلى تطبيقك بطريقة onCreate() للنشاط بدلاً من ذلك، استخدِم منطقًا مشابهًا لما يلي:

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

بعد ذلك، حمِّل الصفحة:

Kotlin

myWebView.loadUrl("http://www.example.com")

Java

myWebView.loadUrl("https://www.example.com");

أو تحميل عنوان URL من سلسلة HTML:

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

يجب أن يتصل تطبيقك بالإنترنت. للاتصال بالإنترنت، يجب طلب إذن INTERNET في ملف البيان، كما هو موضّح في المثال التالي:

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

يمكنك تخصيص WebView من خلال تنفيذ أيّ من الإجراءات التالية:

  • تفعيل إتاحة وضع ملء الشاشة باستخدام WebChromeClient. ويتم استدعاء هذه الفئة أيضًا عندما تحتاج WebView إلى إذن لتغيير واجهة المستخدم الخاصة بالتطبيق المضيف، مثل إنشاء النوافذ أو إغلاقها أو إرسال مربّعات حوار JavaScript إلى المستخدم. لمزيد من المعلومات عن تصحيح الأخطاء في هذا السياق، اطّلِع على تصحيح أخطاء تطبيقات الويب.
  • التعامل مع الأحداث التي تؤثر في عرض المحتوى، مثل الأخطاء المتعلقة بعمليات إرسال النماذج أو التنقّل باستخدام WebViewClient ويمكنك أيضًا استخدام هذه الفئة الفرعية لاعتراض تحميل عناوين URL.
  • تفعيل JavaScript من خلال تعديل السمة WebSettings
  • استخدام JavaScript للوصول إلى كائنات إطار عمل Android التي أدخلتها في WebView

استخدام JavaScript في WebView

إذا كانت صفحة الويب التي تريد تحميلها في WebView تستخدم JavaScript، عليك تفعيل JavaScript في WebView. بعد تفعيل JavaScript، يمكنك إنشاء واجهات بين رمز تطبيقك ورمز JavaScript.

تفعيل JavaScript

تكون JavaScript غير مفعّلة تلقائيًا في WebView. يمكنك تفعيلها من خلال WebSettings المرفق بـ "WebView". يمكنك استرداد WebSettings باستخدام getSettings()، ثم تفعيل JavaScript باستخدام setJavaScriptEnabled().

اطّلِع على المثال التالي:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

ويوفر WebSettings إمكانية الوصول إلى مجموعة متنوعة من الإعدادات الأخرى التي قد تجدها مفيدة. على سبيل المثال، إذا كنت تعمل على تطوير تطبيق ويب مصمّم خصيصًا لـ WebView في تطبيق Android، يمكنك تحديد سلسلة مخصّصة لوكيل المستخدم باستخدام setUserAgentString()، وبعد ذلك يمكنك الاستعلام عن وكيل المستخدم المخصّص في صفحة الويب للتحقّق من أنّ العميل الذي يطلب صفحة الويب هو تطبيق Android.

ربط رمز JavaScript برمز Android

عند تطوير تطبيق ويب مصمّم خصيصًا لـ WebView في تطبيق Android، يمكنك إنشاء واجهات بين رمز JavaScript ورمز Android من جهة العميل. على سبيل المثال، يمكن أن يستدعي رمز JavaScript طريقة في رمز Android لعرض Dialog، بدلاً من استخدام دالة alert() في JavaScript.

لربط واجهة جديدة بين رمز JavaScript ورمز Android، يمكنك استدعاء addJavascriptInterface() لربطه بمثيل فئة لربطه بلغة JavaScript واسم واجهة يمكن أن تطلبه لغة JavaScript للوصول إلى الفئة.

على سبيل المثال، يمكنك تضمين الفئة التالية في تطبيق Android:

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

في هذا المثال، تسمح الفئة WebAppInterface لصفحة الويب بإنشاء رسالة Toast، باستخدام الطريقة showToast().

ويمكنك ربط هذه الفئة بJavaScript الذي يتم تشغيله في WebView باستخدام السمة addJavascriptInterface()، كما هو موضّح في المثال التالي:

Kotlin

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Java

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

تؤدي هذه الخطوة إلى إنشاء واجهة باسم Android لترميز JavaScript الذي يتم تشغيله في WebView. في هذه المرحلة، سيتمكن تطبيق الويب من الوصول إلى فئة WebAppInterface. على سبيل المثال، إليك بعض ترميز HTML وJavaScript الذي ينشئ رسالة إشعار منبثق باستخدام الواجهة الجديدة عندما ينقر المستخدم على أحد الأزرار:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

ولا حاجة إلى إعداد واجهة Android من JavaScript. تتم إتاحة WebView تلقائيًا لصفحتك على الويب. لذا، عندما ينقر مستخدم على الزر، تستخدم الدالة showAndroidToast() واجهة Android لاستدعاء طريقة WebAppInterface.showToast().

التعامل مع التنقل في الصفحات

عندما ينقر المستخدم على رابط من صفحة ويب في WebView، يشغِّل Android تلقائيًا تطبيقًا يعالج عناوين URL. ويتم عادةً فتح متصفّح الويب التلقائي وتحميل عنوان URL المقصود. ويمكنك إلغاء هذا السلوك في WebView ليتم فتح الروابط داخل WebView. يمكنك بعد ذلك السماح للمستخدم بالانتقال للأمام وللخلف من خلال سجلّ صفحات الويب الخاص به الذي يتم الاحتفاظ به بواسطة WebView.

لفتح الروابط التي نقر عليها المستخدم، قدِّم WebViewClient لـ WebView باستخدام setWebViewClient(). يتم تحميل جميع الروابط التي ينقر عليها المستخدم في WebView. إذا أردت التحكّم بشكل أكبر في مكان تحميل الرابط الذي تم النقر عليه، يمكنك إنشاء WebViewClient خاص بك تلغي طريقة shouldOverrideUrlLoading(). يفترض المثال التالي أن MyWebViewClient هي فئة داخلية في Activity.

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

بعد ذلك، يمكنك إنشاء مثيل من WebViewClient الجديد في WebView:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

الآن، عندما ينقر المستخدم على رابط، يستدعي النظام طريقة shouldOverrideUrlLoading() التي تتحقّق مما إذا كان مضيف عنوان URL يتطابق مع نطاق معيّن، كما هو موضّح في المثال السابق. وفي حال التطابق، تعرض الطريقة خطأ ولا تحلّ محلّ تحميل عنوان URL. ويسمح لعنوان URL WebView بتحميل عنوان URL كالمعتاد. وفي حال عدم تطابُق مضيف عنوان URL، سيتم إنشاء Intent لتشغيل الإعداد التلقائي Activity لمعالجة عناوين URL، والذي يتم ربطه بمتصفّح الويب التلقائي لدى المستخدم.

التعامل مع عناوين URL المخصَّصة

يُطبِّق WebView القيود عند طلب الموارد وحلّ الروابط التي تستخدم مخطط عنوان URL مخصّصًا. على سبيل المثال، في حال تنفيذ استدعاءات مثل shouldOverrideUrlLoading() أو shouldInterceptRequest()، ستستدعيها WebView لعناوين URL الصالحة فقط.

على سبيل المثال، قد لا يستدعي WebView طريقة shouldOverrideUrlLoading() للحصول على روابط مثل هذه:

<a href="showProfile">Show Profile</a>

يتم التعامل مع عناوين URL غير الصالحة، مثل العنوان المعروض في المثال السابق، بشكل غير متسق في WebView، لذلك ننصح باستخدام عنوان URL بتنسيق صحيح بدلاً من ذلك. يمكنك استخدام مخطط مخصّص أو عنوان URL يستخدم HTTPS لنطاق تتحكم فيه مؤسستك.

بدلاً من استخدام سلسلة بسيطة في رابط، كما في المثال السابق، يمكنك استخدام مخطَّط مخصّص مثل ما يلي:

<a href="example-app:showProfile">Show Profile</a>

ويمكنك بعد ذلك التعامل مع عنوان URL هذا بطريقة shouldOverrideUrlLoading() على النحو التالي:

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

تهدف واجهة برمجة التطبيقات shouldOverrideUrlLoading() في الأساس إلى إطلاق أهداف لعناوين URL محدّدة. عند تنفيذها، احرص على عرض false لعناوين URL التي تتضمن الأسماء المعرِّفة WebView. ومع ذلك، فأنت لا تقتصر على نوايا إطلاق المنتجات. يمكنك استبدال أغراض الإطلاق بأي سلوك مخصّص في عيّنات التعليمات البرمجية السابقة.

عندما يلغي WebView تحميل عنوان URL، يتم تلقائيًا تجميع سجلّ من صفحات الويب التي تمت زيارتها. يمكنك التنقّل للأمام وللخلف باستخدام السجلّ باستخدام goBack() و goForward().

على سبيل المثال، يوضّح ما يلي طريقة استخدام Activity لزر الرجوع في الجهاز للرجوع إلى الخلف:

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

إذا كان تطبيقك يستخدم الإصدار 1.6.0 من نظام التشغيل AndroidX AppCompat أو الإصدارات الأحدث، يمكنك تبسيط المقتطف السابق بشكل أكبر:

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

تعرض الطريقة canGoBack() القيمة "صحيح" إذا كان هناك سجلّ صفحة ويب يمكن للمستخدم زيارته. وبالمثل، يمكنك استخدام canGoForward() للتحقق مما إذا كان هناك سجلّ إعادة توجيه. إذا لم يتم إجراء عملية الفحص هذه، لن يتم اتّخاذ أي إجراء بعد وصول المستخدم إلى نهاية السجلّ، وذلك من خلال goBack() وgoForward().

التعامل مع التغييرات على إعدادات الجهاز

أثناء وقت التشغيل، تحدث تغييرات حالة النشاط عندما تتغير إعدادات الجهاز، على سبيل المثال عند تدوير الجهاز أو إغلاق أحد محرري أساليب الإدخال (IME). تؤدي هذه التغييرات إلى إتلاف نشاط كائن WebView وإنشاء نشاط جديد، ما يؤدي أيضًا إلى إنشاء عنصر WebView جديد يحمّل عنوان URL للعنصر الذي تم إتلافه. لتعديل السلوك التلقائي لنشاطك، يمكنك تغيير طريقة التعامل مع تغييرات orientation في ملف البيان. لمزيد من المعلومات حول التعامل مع تغييرات الضبط أثناء وقت التشغيل، يمكنك الاطّلاع على تغييرات ضبط الاسم المعرِّف.

إدارة النوافذ

يتم تلقائيًا تجاهل طلبات فتح النوافذ الجديدة. ويتم ذلك سواء تم فتحها باستخدام JavaScript أو من خلال السمة المستهدفة في رابط. يمكنك تخصيص WebChromeClient لتقديم سلوكك الخاص عند فتح نوافذ متعددة.

للحفاظ على أمان تطبيقك، يُفضَّل منع فتح النوافذ المنبثقة والنوافذ الجديدة. والطريقة الأكثر أمانًا لتنفيذ هذا السلوك هي تمرير "true" إلى setSupportMultipleWindows() بدون تجاوز الطريقة onCreateWindow() التي تعتمد عليها setSupportMultipleWindows(). يمنع هذا المنطق تحميل أي صفحة تستخدم target="_blank" في روابطها.