WebView chạy nội dung web trong một quy trình kết xuất riêng. Đôi khi, quy trình này có thể bị chấm dứt. Điều này xảy ra nếu hệ thống tắt trình kết xuất để thu hồi bộ nhớ hoặc nếu chính quy trình đó gặp sự cố. Nếu bạn không xử lý những trường hợp chấm dứt này, ứng dụng của bạn cũng sẽ thoát.
Tại sao việc xử lý trường hợp chấm dứt quy trình lại quan trọng
Trên các phiên bản Android cũ hơn, người dùng ít có khả năng nhận thấy vấn đề khi một ứng dụng không xử lý được trường hợp chấm dứt quy trình kết xuất. Tuy nhiên, các phiên bản Android mới hơn sử dụng các cơ chế phức tạp hơn để quản lý mức tiêu thụ tài nguyên của các quy trình ở chế độ nền. Do đó, nếu ứng dụng của bạn không triển khai trình xử lý cho
onRenderProcessGone, thì giờ đây, người dùng có nhiều khả năng sẽ thấy các trường hợp chấm dứt ở nền trước khi quay lại ứng dụng. Bạn phải triển khai API Xử lý trường hợp chấm dứt để đảm bảo ứng dụng của bạn khôi phục một cách linh hoạt và duy trì trải nghiệm người dùng ổn định.
Sử dụng API Xử lý trường hợp chấm dứt
Bạn có thể xử lý những trường hợp này bằng cách ghi đè onRenderProcessGone trong WebViewClient.
Các yêu cầu chính đối với việc khôi phục
Để ứng dụng của bạn tiếp tục chạy sau khi một quy trình kết xuất chấm dứt, mỗi WebView trong ứng dụng của bạn phải tuân theo các quy tắc sau:
- Không sử dụng lại thực thể WebView. Sau khi trình kết xuất biến mất, đối tượng WebView cụ thể đó sẽ vô dụng. Bạn phải xoá đối tượng WebView khỏi hệ phân cấp khung hiển thị, huỷ đối tượng đó và xoá mọi tham chiếu đến đối tượng đó trong Hoạt động hoặc Mảnh để tránh vô tình tiếp tục gọi các phương thức trên thực thể WebView đã huỷ.
- Tạo một thực thể mới. Nếu muốn tiếp tục hiển thị nội dung web, bạn cần một thực thể WebView mới.
- Trả về true. Quá trình triển khai của bạn phải trả về
trueđể cho mã WebView biết rằng bạn đã xử lý trường hợp này. Nếu bất kỳWebViewnào sử dụng quy trình kết xuất đó không trả vềtrue, thì WebView sẽ tắt hoặc làm cho ứng dụng của bạn gặp sự cố tương ứng với lý do thoát quy trình kết xuất.
Ví dụ về cách triển khai
Ví dụ sau đây cho thấy cách xử lý những trường hợp chấm dứt này trong một Hoạt động hoàn chỉnh. Ví dụ này sử dụng detail.didCrash() để ghi lại xem quy trình có gặp sự cố hay không hoặc liệu hệ thống có tắt trình kết xuất để thu hồi bộ nhớ hay không. Bất kể nguyên nhân là gì, mã sẽ dọn dẹp bằng cách xoá WebView không hoạt động khỏi bố cục, huỷ đối tượng đó, và xoá các tham chiếu của Hoạt động để ngăn NullPointerException.
Sau đó, mã sẽ chuẩn bị một thực thể mới và trả về true để ứng dụng không đóng.
Lưu ý: Ví dụ sau đây luôn trả về true để ứng dụng tiếp tục chạy.
Nếu bạn muốn ứng dụng của mình chấm dứt khi trình kết xuất WebView chấm dứt, bạn có thể trả về false từ lệnh gọi lại này. Tuy nhiên, bạn nên xử lý việc tạo lại và trả về true để mang lại trải nghiệm người dùng ổn định.
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
}
}
Điểm quan trọng cần lưu ý
Khi triển khai onRenderProcessGone, hãy lưu ý những điểm sau để đảm bảo độ ổn định của ứng dụng và duy trì khả năng hiển thị các sự kiện chấm dứt:
- Sự cố lặp lại: Nếu một trình kết xuất gặp sự cố khi tải một trang cụ thể, đừng chỉ tải lại ngay trang đó. Việc này có thể khiến WebView mới gặp sự cố trở lại.
- Khả năng hiển thị: Các trường hợp tắt ứng dụng do thiếu lệnh gọi lại này chưa được phản ánh trong báo cáo sự cố của Play Console. Mặc dù các bản cập nhật trong tương lai có thể giải quyết khoảng trống này, nhưng bạn phải giải quyết những vấn đề này ngay bây giờ để đảm bảo độ ổn định của ứng dụng. Việc triển khai trình xử lý này giúp bạn thấy ngay lý do ứng dụng của mình thoát.