สร้างเว็บแอปใน 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 API ที่ไม่พร้อมใช้งานสำหรับแพลตฟอร์มเวอร์ชันเก่าได้

เพิ่มลงในไฟล์ 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() ตาม ที่แสดงในตัวอย่างต่อไปนี้

KotlinJava
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() ของกิจกรรมแทน ให้ใช้ ตรรกะที่คล้ายกับตัวอย่างต่อไปนี้

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

จากนั้นโหลดหน้าเว็บโดยทำดังนี้

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

หรือโหลด URL จากสตริง HTML ดังนี้

KotlinJava
// 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 ดังที่แสดงในตัวอย่างต่อไปนี้

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

คุณปรับแต่ง WebView ได้โดยทำอย่างใดอย่างหนึ่งต่อไปนี้

  • การเปิดใช้งานการสนับสนุนแบบเต็มหน้าจอโดยใช้ WebChromeClient ชั้นเรียนนี้ จะมีการเรียกเมื่อ WebView ต้องการสิทธิ์ในการแก้ไข UI ของแอปโฮสต์ เช่น การสร้างหรือการปิดหน้าต่างหรือส่งกล่องโต้ตอบ 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()

โปรดดูตัวอย่างต่อไปนี้

KotlinJava
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 ของคุณ คุณสามารถกำหนดสตริง User Agent ที่กําหนดเองด้วย setUserAgentString() จากนั้นค้นหา User Agent ที่กําหนดเองในหน้าเว็บเพื่อยืนยันว่าไคลเอ็นต์ที่ขอหน้าเว็บของคุณเป็นแอป Android

เชื่อมโยงโค้ด JavaScript กับโค้ด Android

เมื่อพัฒนาเว็บแอปพลิเคชันที่ออกแบบมาเพื่อ WebView โดยเฉพาะ ในแอป Android คุณจะสามารถสร้างอินเทอร์เฟซระหว่างโค้ด JavaScript กับ โค้ด Android ฝั่งไคลเอ็นต์ ตัวอย่างเช่น โค้ด JavaScript สามารถเรียกเมธอดใน โค้ด Android เพื่อแสดง Dialog แทนการใช้ฟังก์ชัน alert() ของ JavaScript

หากต้องการเชื่อมโยงอินเทอร์เฟซใหม่ระหว่าง JavaScript กับโค้ด Android ให้เรียกใช้ addJavascriptInterface() โดยส่งอินสแตนซ์คลาสไปให้เพื่อเชื่อมโยงกับ JavaScript และชื่ออินเทอร์เฟซที่ JavaScript สามารถเรียกใช้เพื่อเข้าถึงคลาส

เช่น คุณสามารถใส่คลาสต่อไปนี้ในแอป Android

KotlinJava
/** 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 จะอนุญาตให้หน้าเว็บสร้าง Toast ข้อความ โดยใช้ showToast()

คุณสามารถเชื่อมโยงคลาสนี้กับ JavaScript ที่ทำงานใน WebView ด้วย addJavascriptInterface() ดังที่แสดงในตัวอย่างต่อไปนี้

KotlinJava
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 สำหรับ 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

KotlinJava
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:

KotlinJava
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

เมื่อผู้ใช้แตะลิงก์ ระบบจะเรียกใช้เมธอด shouldOverrideUrlLoading() ซึ่งจะตรวจสอบว่าโฮสต์ URL ตรงกับโดเมนที่เฉพาะเจาะจงหรือไม่ ตามที่ระบุไว้ในตัวอย่างก่อนหน้านี้ หากตรงกันแล้ว เมธอดจะส่งคืนค่า "เท็จ" และจะไม่ลบล้างการโหลด URL ซึ่งช่วยให้ WebView จะโหลด URL ตามปกติ หากโฮสต์ URL ไม่ตรงกัน ระบบจะสร้าง Intent เพื่อเปิด Activity เริ่มต้นสำหรับการจัดการ URL ซึ่งจะเปลี่ยนเส้นทางไปยังเว็บเบราว์เซอร์เริ่มต้นของผู้ใช้

จัดการ URL ที่กำหนดเอง

WebView ใช้ข้อจำกัดเมื่อขอทรัพยากรและแก้ไขลิงก์ที่ใช้รูปแบบ URL ที่กําหนดเอง ตัวอย่างเช่น หากคุณใช้ Callback เช่น shouldOverrideUrlLoading() หรือ shouldInterceptRequest() จากนั้น WebView จะเรียกใช้หน่วยโฆษณาสำหรับ URL ที่ถูกต้องเท่านั้น

เช่น WebView อาจไม่เรียกใช้เมธอด shouldOverrideUrlLoading() สำหรับลิงก์เช่นนี้

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

ระบบจะจัดการ URL ที่ไม่ถูกต้อง เช่น URL ที่แสดงในตัวอย่างก่อนหน้านี้อย่างไม่สอดคล้องกันใน WebView เราจึงขอแนะนำให้ใช้ URL ที่มีรูปแบบถูกต้องแทน คุณสามารถใช้รูปแบบที่กำหนดเองหรือ URL ของ HTTPS สำหรับโดเมนที่องค์กรของคุณควบคุมได้

คุณสามารถใช้รูปแบบที่กำหนดเองแทนการใช้สตริงธรรมดาในลิงก์ได้ ดังตัวอย่างก่อนหน้านี้ ดังนี้

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

จากนั้นคุณจัดการ URL นี้ในเมธอด shouldOverrideUrlLoading() ได้ดังนี้

KotlinJava
// 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 มีไว้เพื่อเปิดใช้งาน Intent สำหรับ URL ที่เฉพาะเจาะจงเป็นหลัก เมื่อใช้งาน ให้ตรวจสอบว่าได้ส่งกลับ false สำหรับ URL ที่ WebView จัดการ อย่างไรก็ตาม คุณไม่ได้เพียงแค่เปิดใช้งาน Intent คุณสามารถ แทนที่ Intent ที่เปิดใช้งานด้วยลักษณะการทำงานที่กำหนดเองในโค้ดก่อนหน้า ตัวอย่าง

เมื่อ WebView ลบล้างการโหลด URL ระบบจะรวบรวม URL ประวัติของหน้าเว็บที่เข้าชม คุณย้อนกลับและไปข้างหน้าในประวัติได้ด้วย goBack() และ goForward()

ตัวอย่างเช่น ตัวอย่างต่อไปนี้แสดงวิธีที่ Activity ใช้ปุ่มย้อนกลับของอุปกรณ์เพื่อไปยังส่วนต่างๆ ย้อนหลัง

KotlinJava
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 ขึ้นไป คุณสามารถลดความซับซ้อนของ ข้อมูลเพิ่มเติม:

KotlinJava
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() แสดงค่า "จริง" หากมีประวัติหน้าเว็บให้ผู้ใช้เข้าชม ในทำนองเดียวกัน คุณสามารถใช้ canGoForward() เพื่อตรวจสอบว่ามีประวัติการเดินหน้าหรือไม่ หากคุณไม่ดำเนินการตรวจสอบนี้ หลังจากที่ผู้ใช้ดูจนจบประวัติ goBack() และ goForward() จะดำเนินการ ไม่มี

จัดการการเปลี่ยนแปลงการกำหนดค่าอุปกรณ์

ในระหว่างรันไทม์ สถานะกิจกรรมจะเปลี่ยนแปลงเมื่อการกำหนดค่าของอุปกรณ์เปลี่ยนแปลง เช่น เมื่อผู้ใช้หมุนอุปกรณ์หรือปิดเครื่องมือแก้ไขวิธีการป้อนข้อมูล (IME) การเปลี่ยนแปลงเหล่านี้จะทำให้กิจกรรมของออบเจ็กต์ WebView ถูกทำลายและสร้างกิจกรรมใหม่ ซึ่งจะสร้างออบเจ็กต์ WebView ใหม่ด้วยซึ่งจะโหลด URL ของออบเจ็กต์ที่ถูกทำลาย หากต้องการแก้ไขลักษณะการทำงานเริ่มต้นของกิจกรรม ให้เปลี่ยนวิธีจัดการการเปลี่ยนแปลง orientation ในไฟล์ Manifest เพื่อดูข้อมูลเพิ่มเติม เกี่ยวกับการจัดการการเปลี่ยนแปลงการกำหนดค่าระหว่างรันไทม์ โปรดอ่านการกำหนดค่าแฮนเดิล การเปลี่ยนแปลง

จัดการหน้าต่าง

โดยค่าเริ่มต้น ระบบจะละเว้นคําขอเปิดหน้าต่างใหม่ โดยจะเป็นจริงไม่ว่าผู้ลงโฆษณา เปิดด้วย JavaScript หรือโดยแอตทริบิวต์เป้าหมายในลิงก์ คุณสามารถปรับแต่ง WebChromeClient เพื่อกำหนดลักษณะการทำงานของคุณเองสำหรับการเปิดหน้าต่างหลายรายการ

หากต้องการรักษาแอปให้ปลอดภัยมากขึ้น คุณควรป้องกันไม่ให้ป๊อปอัปและหน้าต่างใหม่ เมื่อเปิด วิธีที่ปลอดภัยที่สุดในการใช้ลักษณะการทำงานนี้คือการส่งผ่าน "true" ไปยัง setSupportMultipleWindows() แต่ไม่แทนที่ onCreateWindow() ซึ่ง setSupportMultipleWindows() จะขึ้นอยู่กับเมธอด ตรรกะนี้จะป้องกัน หน้าที่ใช้ target="_blank" ในลิงก์ไม่ให้โหลด