จัดการการสิ้นสุด WebView

WebView เรียกใช้เนื้อหาเว็บในกระบวนการโปรแกรมแสดงผลแยกต่างหาก บางครั้งกระบวนการนี้อาจสิ้นสุดลง กรณีนี้จะเกิดขึ้นหากระบบปิดโปรแกรมแสดงผลเพื่อ เรียกคืนหน่วยความจำ หรือหากกระบวนการดังกล่าวขัดข้อง หากคุณไม่จัดการการสิ้นสุดเหล่านี้ แอปของคุณก็จะออกไปด้วย

เหตุใดการจัดการการสิ้นสุดกระบวนการจึงสำคัญ

ใน Android เวอร์ชันที่ต่ำกว่า ผู้ใช้มีแนวโน้มที่จะไม่สังเกตเห็นปัญหาเมื่อ แอปจัดการการสิ้นสุดกระบวนการแสดงผลไม่สำเร็จ อย่างไรก็ตาม Android เวอร์ชันใหม่กว่าจะใช้กลไกที่ซับซ้อนกว่าในการจัดการการใช้ทรัพยากรของกระบวนการที่ทำงานเบื้องหลัง ดังนั้น หากแอปของคุณไม่ได้ใช้ตัวแฮนเดิลสำหรับ onRenderProcessGone ผู้ใช้มีแนวโน้มที่จะเห็นการสิ้นสุดของ การทำงานในเบื้องหน้าเมื่อกลับมาที่แอปมากขึ้น คุณต้องใช้ Termination Handling API เพื่อให้แน่ใจว่าแอปจะกู้คืนได้อย่างราบรื่นและรักษา ประสบการณ์การใช้งานที่เสถียรของผู้ใช้

ใช้ Termination Handling API

คุณจัดการสถานการณ์เหล่านี้ได้โดยการลบล้าง onRenderProcessGone ใน WebViewClient

ข้อกำหนดสำคัญสำหรับการกู้คืน

หากต้องการให้แอปทำงานต่อไปหลังจากกระบวนการตัวแสดงผลสิ้นสุดลง WebView แต่ละรายการในแอปต้องเป็นไปตามกฎต่อไปนี้

  • อย่าใช้อินสแตนซ์ WebView ซ้ำ เมื่อไม่มีโปรแกรมแสดงผลแล้ว ออบเจ็กต์ WebView ที่เฉพาะเจาะจงนั้นจะใช้ไม่ได้ คุณต้องนำออบเจ็กต์ WebView ออกจากลำดับชั้นของมุมมอง ทำลายออบเจ็กต์ และล้างการอ้างอิงทั้งหมดใน Activity หรือ Fragment เพื่อหลีกเลี่ยงการเรียกเมธอดในอินสแตนซ์ WebView ที่ถูกทำลายแล้วโดยไม่ตั้งใจ
  • สร้างอินสแตนซ์ใหม่ หากต้องการแสดงเนื้อหาเว็บต่อไป คุณจะต้องมีอินสแตนซ์ WebView ใหม่
  • ส่งคืนค่าเป็นจริง การติดตั้งใช้งานของคุณต้องแสดงผล true เพื่อบอกโค้ด WebView ว่าคุณได้จัดการสถานการณ์แล้ว หาก WebView ใดก็ตามที่ใช้กระบวนการแสดงผลนั้นไม่แสดงผล true WebView จะปิดหรือทำให้แอปขัดข้องตามเหตุผลที่กระบวนการแสดงผลสิ้นสุดลง

ตัวอย่างการใช้งาน

ตัวอย่างต่อไปนี้แสดงวิธีจัดการการสิ้นสุดเหล่านี้ภายในกิจกรรมที่สมบูรณ์ โดยจะใช้ detail.didCrash() เพื่อบันทึกว่ากระบวนการขัดข้องหรือไม่ หรือระบบปิดการแสดงผลเพื่อเรียกคืนหน่วยความจำ ไม่ว่าสาเหตุจะเป็นอะไรก็ตาม โค้ดจะล้างข้อมูลโดยนำ WebView ที่ไม่ได้ใช้งานออกจากเลย์เอาต์ ทำลาย WebView และล้างข้อมูลอ้างอิงของกิจกรรมเพื่อป้องกันไม่ให้เกิด NullPointerException จากนั้นจะเตรียมอินสแตนซ์ใหม่และแสดง true เพื่อไม่ให้แอปปิด

หมายเหตุ: ตัวอย่างต่อไปนี้จะแสดงผล true เสมอเพื่อให้แอปทำงานต่อไป หากต้องการให้แอปสิ้นสุดเมื่อตัวแสดงผล WebView สิ้นสุด คุณสามารถส่งคืน false จากการเรียกกลับนี้ได้ อย่างไรก็ตาม เราขอแนะนำให้ จัดการการสร้างใหม่และส่งคืน true เพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่เสถียร

class MyWebViewActivity : AppCompatActivity() {

    private var webViewContainer: ViewGroup? = null
    private var myWebView: WebView? = null

    private var crashCount = 0
    private val MAX_CRASHES = 3

    // NEW FLAG: Tracks if the WebView crashed while the activity was hidden
    private var isWebViewPendingRecovery = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my_web_view)
        webViewContainer = findViewById(R.id.my_web_view_container)

        initWebView()
    }

    // NEW LIFECYCLE HOOK: Triggers when the user brings the app back to the foreground
    override fun onResume() {
        super.onResume()
        // If a crash happened in the background, rebuild it now that the user can see it
        if (isWebViewPendingRecovery) {
            isWebViewPendingRecovery = false
            Log.i("MY_APP_TAG", "Activity resumed. Recovering pending WebView now.")
            recoverWebView()
        }
    }

    private fun initWebView() {
        val webView = WebView(this)
        myWebView = webView

        webViewContainer?.addView(webView)

        webView.webViewClient = object : WebViewClient() {
            override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
                if (detail.didCrash()) {
                    Log.e("MY_APP_TAG", "The WebView rendering process crashed!")
                } else {
                    Log.e("MY_APP_TAG", "System killed the WebView rendering process to reclaim memory.")
                }

                // Safely clean up the dead WebView
                webViewContainer?.removeView(webView)
                webView.destroy()
                myWebView = null

                // MODIFIED LOGIC: Check the lifecycle state of the Activity
                // lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) means the activity is visible and active
                if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
                    Log.i("MY_APP_TAG", "Activity is in foreground. Recovering immediately.")
                    recoverWebView()
                } else {
                    // The app is in the background. Mark it for later!
                    Log.w("MY_APP_TAG", "Activity is in background. Postponing recovery until user returns.")
                    isWebViewPendingRecovery = true
                }

                return true
            }
        }
        webView.loadUrl("https://www.example.com")
    }

    // HELPER FUNCTION: Extracted the recovery logic to reuse it
    private fun recoverWebView() {
        if (crashCount < MAX_CRASHES) {
            crashCount++
            Log.w("MY_APP_TAG", "Recovering WebView... Attempt $crashCount")
            initWebView()
        } else {
            Log.e("MY_APP_TAG", "WebView crashed too many times. Showing error UI.")
            showErrorUI()
        }
    }

    private fun showErrorUI() {
        // Example: Inflate an error view
    }

    override fun onDestroy() {
        super.onDestroy()
        myWebView?.let {
            webViewContainer?.removeView(it)
            it.destroy()
        }
        myWebView = null
    }
}

สิ่งสำคัญที่ควรพิจารณา

เมื่อติดตั้งใช้งาน onRenderProcessGone โปรดคำนึงถึงข้อควรพิจารณาต่อไปนี้ เพื่อให้มั่นใจว่าแอปจะมีความเสถียรและรักษาการมองเห็นเหตุการณ์การสิ้นสุด

  • ข้อขัดข้องซ้ำๆ: หากตัวแสดงผลขัดข้องขณะโหลดหน้าเว็บที่เฉพาะเจาะจง อย่าเพิ่งโหลดหน้าเว็บเดียวกันซ้ำทันที ซึ่งอาจทำให้ WebView ใหม่ ขัดข้องอีกครั้ง
  • ระดับการเข้าถึง: การปิดแอปที่เกิดจากการไม่มีการเรียกกลับนี้ยังไม่แสดงในรายงานข้อขัดข้องของ Play Console แม้ว่าการอัปเดตในอนาคตอาจช่วยแก้ไขช่องโหว่นี้ได้ แต่คุณต้องแก้ไขปัญหาเหล่านี้ตอนนี้เพื่อให้มั่นใจว่าแอปจะมีความเสถียร การใช้ตัวแฮนเดิลนี้จะช่วยให้คุณทราบได้ทันทีว่าเหตุใดแอปจึงออก