ساخت برنامه های وب در WebView

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

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

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

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

کار با WebView در نسخه‌های قبلی اندروید

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

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

کاتلین

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

گرووی

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

برای جزئیات بیشتر، مثال WebView را در GitHub بررسی کنید.

یک WebView به برنامه خود اضافه کنید

برای افزودن یک WebView به برنامه‌تان، می‌توانید عنصر <WebView> را در طرح‌بندی فعالیت خود قرار دهید یا کل پنجره Activity را در onCreate() به عنوان یک WebView تنظیم کنید.

یک WebView در طرح‌بندی فعالیت اضافه کنید

برای افزودن یک 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");

یک WebView در onCreate() اضافه کنید

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

کاتلین

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

یا URL را از یک رشته 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 برای تغییر رابط کاربری برنامه میزبان، مانند ایجاد یا بستن پنجره‌ها یا ارسال دیالوگ‌های جاوا اسکریپت به کاربر، به مجوز نیاز داشته باشد. برای کسب اطلاعات بیشتر در مورد اشکال‌زدایی در این زمینه، Debug web apps را مطالعه کنید.
  • مدیریت رویدادهایی که بر رندر محتوا تأثیر می‌گذارند، مانند خطاها در ارسال فرم یا پیمایش با استفاده از WebViewClient . همچنین می‌توانید از این زیرکلاس برای رهگیری بارگذاری URL استفاده کنید.
  • فعال کردن جاوا اسکریپت با تغییر WebSettings .
  • استفاده از جاوا اسکریپت برای دسترسی به اشیاء چارچوب اندروید که به یک 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 در برنامه اندروید شما طراحی شده است، می‌توانید یک رشته عامل کاربر سفارشی را با setUserAgentString() تعریف کنید، سپس عامل کاربر سفارشی را در صفحه وب خود جستجو کنید تا تأیید کنید که کلاینتی که صفحه وب شما را درخواست می‌کند، برنامه اندروید شماست.

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

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

برای اتصال یک رابط جدید بین کد جاوا اسکریپت و اندروید، تابع addJavascriptInterface() را فراخوانی کنید و یک نمونه کلاس برای اتصال به جاوا اسکریپت و یک نام رابط که جاوا اسکریپت شما می‌تواند برای دسترسی به کلاس آن را فراخوانی کند، به آن ارسال کنید.

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

کاتلین

/** 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 ایجاد کند.

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

کاتلین

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 و جاوا اسکریپت وجود دارد که وقتی کاربر روی یک دکمه ضربه می‌زند، با استفاده از رابط کاربری جدید یک پیام Toast ایجاد می‌کند:

<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 شما ضربه می‌زند، به‌طور پیش‌فرض، اندروید برنامه‌ای را اجرا می‌کند که URLها را مدیریت می‌کند. معمولاً مرورگر وب پیش‌فرض، URL مقصد را باز کرده و بارگذاری می‌کند. با این حال، می‌توانید این رفتار را برای 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() را فراخوانی می‌کند که بررسی می‌کند آیا میزبان URL با یک دامنه خاص، همانطور که در مثال قبلی تعریف شده است، مطابقت دارد یا خیر. اگر مطابقت داشته باشد، متد مقدار false را برمی‌گرداند و بارگذاری URL را لغو نمی‌کند. این متد به WebView اجازه می‌دهد URL را طبق معمول بارگذاری کند. اگر میزبان URL مطابقت نداشته باشد، یک Intent برای راه‌اندازی Activity پیش‌فرض برای مدیریت URLها ایجاد می‌شود که به مرورگر وب پیش‌فرض کاربر متصل می‌شود.

مدیریت URL های سفارشی

WebView هنگام درخواست منابع و حل لینک‌هایی که از یک طرح URL سفارشی استفاده می‌کنند، محدودیت‌هایی اعمال می‌کند. برای مثال، اگر شما callbackهایی مانند 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() خود به صورت زیر مدیریت کنید:

کاتلین

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

API shouldOverrideUrlLoading() در درجه اول برای راه‌اندازی intentها برای URLهای خاص در نظر گرفته شده است. هنگام پیاده‌سازی آن، مطمئن شوید که برای URLهایی که WebView مدیریت می‌کند، false برمی‌گردانید. با این حال، شما محدود به راه‌اندازی intentها نیستید. می‌توانید launch intentها را با هر رفتار سفارشی در نمونه‌های کد قبلی جایگزین کنید.

وقتی WebView شما بارگذاری URL را لغو می‌کند، به‌طور خودکار تاریخچه‌ای از صفحات وب بازدید شده را جمع‌آوری می‌کند. می‌توانید با استفاده از 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);
}

اگر برنامه شما از AndroidX AppCompat 1.6.0+ استفاده می‌کند، می‌توانید قطعه کد قبلی را حتی بیشتر ساده کنید:

کاتلین

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() هیچ کاری انجام نمی‌دهند.

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

در طول زمان اجرا، تغییرات وضعیت فعالیت زمانی رخ می‌دهد که پیکربندی دستگاه تغییر کند، مانند زمانی که کاربران دستگاه را می‌چرخانند یا یک ویرایشگر روش ورودی (IME) را رد می‌کنند. این تغییرات باعث می‌شود که فعالیت یک شیء WebView از بین برود و یک فعالیت جدید ایجاد شود، که همچنین یک شیء WebView جدید ایجاد می‌کند که URL شیء از بین رفته را بارگذاری می‌کند. برای تغییر رفتار پیش‌فرض فعالیت خود، می‌توانید نحوه مدیریت تغییرات orientation را در مانیفست خود تغییر دهید. برای کسب اطلاعات بیشتر در مورد مدیریت تغییرات پیکربندی در طول زمان اجرا، بخش مدیریت تغییرات پیکربندی را مطالعه کنید.

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

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

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