ساختن برنامه‌های وب در «وب‌نما»

از WebView برای ارائه برنامه وب یا صفحه وب به‌عنوان بخشی از برنامه کارخواه استفاده کنید. کلاس WebView افزونه‌ای از کلاس View Android است که به شما امکان می‌دهد صفحه‌های وب را به‌عنوان بخشی از چیدمان فعالیتتان نمایش دهید. این ویژگی‌های یک مرورگر وب کاملاً توسعه‌یافته، مانند کنترل‌های پیمایش یا نوار نشانی را ندارد. تمام کاری که WebView به‌طور پیش‌فرض انجام می‌دهد، نمایش صفحه وب است.

WebView می‌تواند به شما کمک کند اطلاعاتی را در برنامه‌تان ارائه دهید که ممکن است لازم باشد به‌روز کنید، مثل قرارداد کاربر نهایی یا راهنمای کاربر. در برنامه Android، می‌توانید Activity ایجاد کنید که حاوی WebView باشد، سپس از آن برای نمایش سندتان که به‌صورت آنلاین میزبانی می‌شود استفاده کنید.

WebView همچنین می‌تواند زمانی‌که برنامه شما داده‌هایی را به کاربر ارائه می‌دهد که برای بازیابی داده‌ها به اتصال اینترنت نیاز دارد، مانند ایمیل، کمک کند. در این حالت، ممکن است متوجه شوید که ساختن WebView در برنامه Android که صفحه وب را با همه داده‌های کاربر نشان می‌دهد آسان‌تر از انجام درخواست شبکه، سپس تجزیه داده‌ها و ارائه آن در چیدمان Android است. درعوض، می‌توانید صفحه وبی طراحی کنید که برای دستگاه‌های مجهز به Android سفارشی‌سازی شده باشد و سپس WebView را در برنامه Android خود پیاده‌سازی کنید که صفحه وب را بار می‌کند.

این سند نحوه شروع به کار با WebView، نحوه اتصال جاوا اسکریپت از صفحه وب به کد سمت کارخواه در برنامه Android، نحوه مدیریت پیمایش صفحه، و نحوه مدیریت پنجره‌ها هنگام استفاده از WebView را شرح می‌دهد.

کار با WebView در نسخه‌های قدیمی‌تر Android

برای استفاده ایمن از قابلیت‌های جدیدتر WebView در دستگاهی که برنامه‌تان در آن اجرا می‌شود، کتابخانه Jetpack Webkit را اضافه کنید. این کتابخانه ثابتی است که می‌توانید به برنامه‌تان اضافه کنید تا از میاناهای برنامه‌سازی کاربردی android.webkit که برای نسخه‌های قبلی پلاتفرم دردسترس نیستند استفاده کنید.

آن را به‌صورت زیر به فایل build.gradle خود اضافه کنید:

کاتلین

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

شیک

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

برای جزئیات بیشتر، نمونه WebView را در GitHub کاوش کنید.

افزودن «وب‌نما» به برنامه

برای افزودن WebView به برنامه‌تان، می‌توانید عنصر <WebView> را در چیدمان فعالیتتان اضافه کنید یا کل پنجره Activity را به‌عنوان WebView در onCreate تنظیم کنید.

افزودن «وب‌نما» در چیدمان فعالیت

برای افزودن WebView به برنامه‌تان در چیدمان، کد زیر را به فایل XML چیدمان فعالیتتان اضافه کنید:

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

برای بار کردن صفحه وب در WebView، از loadUrl استفاده کنید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

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

افزودن «وب‌نما» در onCreate()‎

برای افزودن WebView به برنامه‌تان در روش onCreate() فعالیت، از منطقی مشابه زیر استفاده کنید:

کاتلین

val myWebView = WebView(activityContext)
setContentView(myWebView)

جاوا

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

سپس صفحه را بار کنید:

کاتلین

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

جاوا

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

یا نشانی وب را از رشته HTML بار کنید:

کاتلین

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

جاوا

// 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 برای تغییر دادن واسط کاربر برنامه میزبان به اجازه نیاز داشته باشد، مثلاً برای ایجاد یا بستن پنجره‌ها یا ارسال کردن کادرهای گفتگوی جاوا اسکریپت به کاربر. برای کسب اطلاعات بیشتر درباره اشکال‌زدایی در این زمینه، اشکال‌زدایی برنامه‌های وب را بخوانید.
  • رسیدگی به رویدادهایی که بر رندر کردن محتوا تأثیر می‌گذارند، مانند خطاهای ارسال فرم یا پیمایش بااستفاده از WebViewClient. همچنین می‌توانید از این زیرکلاس برای رهگیری بارگیری نشانی وب استفاده کنید.
  • درحال فعال کردن جاوا اسکریپت با اصلاح WebSettings.
  • استفاده از جاوا اسکریپت برای دسترسی به اشیاء چارچوب Android که به WebView تزریق کرده‌اید.

استفاده از جاوا اسکریپت در «وب‌نما»

اگر صفحه وبی که می‌خواهید در WebView بار کنید از جاوا اسکریپت استفاده می‌کند، باید جاوا اسکریپت را برای WebView فعال کنید. پس‌از فعال کردن جاوا اسکریپت، می‌توانید بین کد برنامه و کد جاوا اسکریپت خود میاناهایی ایجاد کنید.

فعال کردن جاوا اسکریپت

جاوا اسکریپت به‌طور پیش‌فرض در WebView غیرفعال است. می‌توانید آن را ازطریق WebSettings پیوست‌شده به WebView فعال کنید. WebSettings را با getSettings بازیابی کنید، سپس جاوا اسکریپت را با setJavaScriptEnabled فعال کنید.

مثال زیر را ببینید:

کاتلین

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

جاوا

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

WebSettings دسترسی به انواع تنظیمات دیگر را که ممکن است برایتان مفید باشد فراهم می‌کند. برای مثال، اگر درحال توسعه برنامه وبی هستید که به‌طور خاص برای WebView در برنامه Android شما طراحی شده است، می‌توانید رشته نماینده کاربر سفارشی را با setUserAgentString تعریف کنید، سپس نماینده کاربر سفارشی را در صفحه وب خود پُرسمان کنید تا تأیید کنید که کارخواه درخواست‌کننده صفحه وب شما برنامه Android شما است.

اتصال کد جاوا اسکریپت به کد Android

هنگام توسعه برنامه وبی که به‌طور خاص برای WebView در برنامه Android شما طراحی شده است، می‌توانید میاناهایی بین کد جاوا اسکریپت و کد Android سمت کارخواه ایجاد کنید. برای مثال، کد جاوا اسکریپت شما می‌تواند روشی را در کد Android شما فراخوانی کند تا به‌جای استفاده از تابع alert() جاوا اسکریپت، Dialog را نمایش دهد.

برای مقید کردن میانای جدید بین کد جاوا اسکریپت و Android، addJavascriptInterface را فرابخوانید و نمونه کلاسی را به آن ارسال کنید تا به جاوا اسکریپت شما مقید شود و نام میانایی را ارسال کنید که جاوا اسکریپت شما بتواند برای دسترسی به کلاس فرابخواند.

برای کسب اطلاعات بیشتر درباره ارتباط بین جاوا اسکریپت و کد بومی، ازجمله میاناهای برنامه‌سازی کاربردی مدرن‌تر و ایمن‌تر، به دسترسی به میاناهای برنامه‌سازی کاربردی بومی با JSBridge مراجعه کنید.

برای مثال، می‌توانید کلاس زیر را در برنامه Android خود اضافه کنید:

کاتلین

/** 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()
    }
}

جاوا

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 به صفحه وب اجازه می‌دهد بااستفاده از روش showToast()، پیام Toast ایجاد کند.

می‌توانید این کلاس را به جاوا اسکریپتی که در WebView شما اجرا می‌شود با addJavascriptInterface() متصل کنید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

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

این کار میانایی به‌نام Android برای جاوا اسکریپت درحال اجرا در WebView ایجاد می‌کند. در این مرحله، برنامه وب شما به کلاس WebAppInterface دسترسی دارد. برای مثال، در اینجا چند HTML و جاوا اسکریپت وجود دارد که هنگامی که کاربر روی دکمه‌ای ضربه می‌زند، بااستفاده از واسط جدید یک پیام موقت ایجاد می‌کند:

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

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

نیازی به مقداردهی اولیه میانای Android از جاوا اسکریپت نیست. ‫WebView به‌طور خودکار آن را برای صفحه وب شما دردسترس قرار می‌دهد. بنابراین، وقتی کاربر روی دکمه ضربه می‌زند، تابع showAndroidToast() از میانای Android برای فراخوانی روش WebAppInterface.showToast() استفاده می‌کند.

مدیریت پیمایش صفحه

وقتی کاربر روی پیوندی از صفحه وب در WebView شما تک‌ضرب می‌زند، به‌طور پیش‌فرض، Android برنامه‌ای را راه‌اندازی می‌کند که نشانی‌های وب را مدیریت می‌کند. معمولاً مرورگر وب پیش‌فرض باز می‌شود و نشانی وب مقصد را بار می‌کند. بااین‌حال، می‌توانید این رفتار را برای WebView لغو کنید تا پیوندها در WebView شما باز شود. سپس می‌توانید به کاربر اجازه دهید در سابقه صفحه وب خود که توسط WebView شما نگهداری می‌شود، به عقب و جلو پیمایش کند.

برای باز کردن پیوندهایی که کاربر روی آن‌ها تک‌ضرب زده است، بااستفاده از setWebViewClient، WebViewClient را برای WebView خود ارائه دهید. همه پیوندهایی که کاربر روی آن‌ها تک‌ضرب می‌زند در WebView شما بار می‌شود. اگر می‌خواهید کنترل بیشتری بر محل بار شدن پیوند کلیک‌شده داشته باشید، WebViewClient خودتان را ایجاد کنید که روش shouldOverrideUrlLoading را لغو می‌کند. مثال زیر فرض می‌کند که MyWebViewClient یک کلاس داخلی از Activity است.

کاتلین

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

جاوا

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 ایجاد کنید:

کاتلین

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

جاوا

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

اکنون وقتی کاربر روی پیوندی تک‌ضرب می‌زند، سیستم روش shouldOverrideUrlLoading را فراخوانی می‌کند که بررسی می‌کند آیا میزبان نشانی وب با دامنه خاصی مطابقت دارد یا نه، همان‌طور که در مثال قبلی تعریف شده است. اگر مطابقت داشته باشد، روش مقدار منطقی FALSE را برمی‌گرداند و بار کردن نشانی وب را لغو نمی‌کند. این گزینه به WebView اجازه می‌دهد نشانی وب را طبق معمول بار کند. اگر میزبان نشانی وب مطابقت نداشته باشد، Intent ایجاد می‌شود تا Activity پیش‌فرض برای رسیدگی به نشانی‌های وب راه‌اندازی شود که به مرورگر وب پیش‌فرض کاربر منتهی می‌شود.

مدیریت نشانی‌های وب سفارشی

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

برای مثال، ممکن است WebView روش shouldOverrideUrlLoading شما را برای پیوندهایی مثل این فراخوانی نکند:

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

نشانی‌های وب نامعتبر، مانند آنچه در مثال قبلی نشان داده شده است، به‌طور ناسازگار در WebView مدیریت می‌شوند، بنابراین توصیه می‌کنیم به‌جای آن از یک نشانی وب با ساختار مناسب استفاده کنید. می‌توانید از طرح‌واره سفارشی یا نشانی وب HTTPS برای دامنه‌ای که سازمانتان کنترل می‌کند استفاده کنید.

به‌جای استفاده از رشته‌ای ساده در پیوند، مانند مثال قبلی، می‌توانید از طرحی سفارشی مانند زیر استفاده کنید:

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

سپس می‌توانید این نشانی وب را در روش shouldOverrideUrlLoading خود به این صورت مدیریت کنید:

کاتلین

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

جاوا

// 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 API در درجه اول برای راه‌اندازی هدف‌ها برای نشانی‌های وب خاص درنظر گرفته شده است. هنگام پیاده‌سازی آن، حتماً false را برای نشانی‌های وب که WebView مدیریت می‌کند برگردانید. البته به راه‌اندازی قصدها محدود نیستید. می‌توانید قصد راه‌اندازی را با هر رفتار سفارشی در نمونه کدهای قبلی جایگزین کنید.

وقتی WebView شما بار کردن نشانی وب را لغو می‌کند، به‌طور خودکار سابقه صفحه‌های وب بازدیدشده را جمع‌آوری می‌کند. می‌توانید با goBack و goForward در سابقه به عقب و جلو پیمایش کنید.

برای مثال، در زیر نشان داده شده است که چگونه Activity شما می‌تواند از دکمه «برگشت» دستگاه برای پیمایش به عقب استفاده کند:

کاتلین

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

جاوا

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

اگر برنامه‌تان از Jetpack AppCompat نسخه ۱.۶.۰ یا بالاتر استفاده می‌کند، می‌توانید قطعه کد قبلی را بیشتر ساده کنید:

کاتلین

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

جاوا

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

اگر سابقه صفحه وب برای بازدید کاربر وجود داشته باشد، روش canGoBack مقدار true را برمی‌گرداند. به‌همین ترتیب، می‌توانید از canGoForward برای بررسی اینکه آیا سابقه جلو رفتن وجود دارد یا نه استفاده کنید. اگر این بررسی را انجام ندهید، پس‌از اینکه کاربر به پایان سابقه رسید، goBack و goForward هیچ کاری انجام نمی‌دهند.

برای حفظ سابقه پیمایش در سراسر بازسازی فعالیت یا بازیابی فرایند پس‌زمینه، از saveState برای سریال‌سازی سابقه پیمایش WebView در Bundle و از restoreState برای بازیابی آن استفاده کنید. برای راهنمایی درباره مدیریت ایمن محدودیت‌های اندازه وضعیت ذخیره‌شده و اجتناب از TransactionTooLargeException، به مدیریت کارآمد وضعیت WebView مراجعه کنید.

مدیریت تغییرات پیکربندی دستگاه

درطول زمان اجرا، وقتی پیکربندی دستگاه تغییر می‌کند، مثلاً وقتی کاربر دستگاه را می‌چرخاند یا ویرایشگر روش ورودی (IME) را می‌بندد، حالت فعالیت تغییر می‌کند. این تغییرات باعث می‌شود فعالیت شیء WebView ازبین برود و فعالیت جدیدی ایجاد شود، که این امر همچنین شیء WebView جدیدی ایجاد می‌کند که نشانی وب شیء ازبین‌رفته را بار می‌کند. می‌توانید از saveState در onSaveInstanceState() و restoreState پس‌از بازآفرینی برای بازیابی بافت پیمایش کاربر استفاده کنید. برای راهبردهای مدیریت ایمن اندازه وضعیت، مدیریت کارآمد وضعیت WebView را ببینید.

برای اصلاح رفتار پیش‌فرض فعالیتتان، می‌توانید نحوه مدیریت تغییرات orientation را در مانیفست خود تغییر دهید. برای کسب اطلاعات بیشتر درباره مدیریت تغییرات پیکربندی درطول زمان اجرا، مدیریت تغییرات پیکربندی را بخوانید.

مدیریت پنجره‌ها

به‌طور پیش‌فرض، درخواست‌های باز کردن پنجره‌های جدید نادیده گرفته می‌شود. این امر چه با جاوا اسکریپت باز شوند چه با مشخصه هدف در پیوند، صادق است. می‌توانید WebChromeClient را سفارشی‌سازی کنید تا رفتار خودتان را برای باز کردن چند پنجره ارائه دهید.

برای ایمن‌تر نگه داشتن برنامه‌تان، بهتر است از باز شدن بالاپرها و پنجره‌های جدید جلوگیری کنید. ایمن‌ترین راه برای پیاده‌سازی این رفتار، ارسال "true" به setSupportMultipleWindows است اما نباید روش onCreateWindow را که setSupportMultipleWindows() به آن وابسته است لغو کنید. این منطق از بار شدن هر صفحه‌ای که از target="_blank" در پیوندهایش استفاده می‌کند جلوگیری می‌کند.

مدیریت حافظه و عملکرد

چون WebView روی موتور مبتنی بر Chromium با فرایندهای جداگانه و کد بومی اجرا می‌شود، استفاده از حافظه مبنای آن بیشتر از نماهای استاندارد Android است. استفاده مجدد از نمونه‌های WebView و ازبین بردن صحیح آن‌ها برای حفظ پایداری برنامه ضروری است.

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