WebView chạy nội dung trên web trong một quy trình kết xuất riêng biệt. Đôi khi, quá 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 để lấy lại bộ nhớ hoặc nếu chính quy trình đó gặp sự cố. Nếu bạn không xử lý các lệnh 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ý chấm dứt quy trình lại quan trọng
Trên các phiên bản Android thấp hơn, người dùng ít có khả năng nhận thấy vấn đề khi ứng dụng không xử lý được việc chấm dứt quy trình trình kết xuất. Tuy nhiên, các phiên bản Android mới hơn sử dụng cơ chế phức tạp hơn để quản lý mức tiêu thụ tài nguyên của các tiến trình nền. Do đó, nếu ứng dụng của bạn không triển khai trình xử lý cho onRenderProcessGone, thì người dùng hiện có nhiều khả năng sẽ thấy các hoạt động chấm dứt ở nền trước khi quay lại ứng dụng. Bạn cần triển khai API Xử lý việc chấm dứt để đảm bảo ứng dụng của bạn khôi phục một cách suôn sẻ và duy trì trải nghiệm người dùng ổn định.
Sử dụng API Xử lý việc 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.
Yêu cầu chính để khôi phục
Để ứng dụng tiếp tục chạy sau khi quá trình trình kết xuất chấm dứt, mỗi WebView trong ứng dụng phải tuân theo các quy tắc sau:
- Không sử dụng lại phiên bản WebView. Sau khi trình kết xuất không còn nữa, đối tượng WebView cụ thể đó sẽ không còn hữu ích. Bạn phải xoá đối tượng WebView khỏi hệ phân cấp view của bạn, huỷ đối tượng đó và xoá mọi tệp tham chiếu đến đối tượng đó trong Hoạt động hoặc Fragment để tránh vô tình tiếp tục gọi các phương thức trên thực thể WebView đã bị huỷ.
- Tạo một thực thể mới. Nếu muốn tiếp tục hiển thị nội dung trên web, bạn cần có một thực thể WebView mới.
- Trả về giá trị 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ý tình huống. Nếu bất kỳWebViewnào sử dụng quy trình trình kết xuất đó không trả vềtrue, thì WebView sẽ tắt hoặc làm ứng dụng của bạn gặp sự cố tương ứng với lý do thoát quy trình 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ý các trường hợp chấm dứt này trong một Hoạt động hoàn chỉnh. Hệ thống 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 để lấy lạ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ỷ WebView đó và xoá các tệp đối chiếu của Hoạt động để ngăn NullPointerException.
Sau đó, hệ thống 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 luôn trả về true để duy trì hoạt động của ứng dụng.
Nếu 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 ổn định cho người dùng.
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ều 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 trình kết xuất gặp sự cố khi tải một trang cụ thể, đừng chỉ tải lại trang đó ngay lập tức. Điều này có thể khiến WebView mới gặp sự cố một lần nữa.
- Chế độ hiển thị: Các trường hợp ứng dụng bị buộc 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 vấn đề này, nhưng bạn phải giải quyết các 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 biết ngay lý do ứng dụng thoát.
- Phân biệt việc chấm dứt trình kết xuất với việc quy trình ứng dụng bị buộc tắt:
onRenderProcessGonexử lý các trường hợp quy trình trình kết xuất bị chấm dứt trong khi quy trình ứng dụng vẫn hoạt động. Nếu toàn bộ quy trình ứng dụng bị hệ thống tắt trong nền, hãy sử dụngonSaveInstanceState()vớiWebView.saveState()vàWebView.restoreState()để duy trì và khôi phục nhật ký điều hướng. Để quản lý trạng thái đã lưu một cách an toàn mà không đạt đến giới hạn giao dịch, hãy xem phần Quản lý trạng thái WebView một cách hiệu quả.