Below I posted my current onRenderProcessGone.
if (!detail.didCrash()) {}
the instance variable "view" is guaranteed to be null, it's safe to reinitialize it. Should I myself reinitialize it or system will do it?Could you specify the example of logic for how the app can continue executing? How to handle the crash more gracefully?
@TargetApi(Build.VERSION_CODES.O)
@Override
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
// WebViewClient.onRenderProcessGone was added in O.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return false;
}
super.onRenderProcessGone(view, detail);
if (!detail.didCrash()) {
// Renderer was killed because the system ran out of memory.
// The app can recover gracefully by creating a new WebView instance
// in the foreground.
Log.e("MY_APP", "System killed the WebView rendering process " +
"to reclaim memory. Recreating...");
if (view != null) {
((ViewGroup)view.getParent()).removeView(view);
view.destroy();
view = null;
}
// By this point, the instance variable "view" is guaranteed
// to be null, so it's safe to reinitialize it.
return true; // The app continues executing.
}
// Renderer crashed because of an internal error, such as a memory
// access violation.
Log.e("MY_APP", "The WebView rendering process crashed!");
// In this example, the app itself crashes after detecting that the
// renderer crashed. If you choose to handle the crash more gracefully
// and allow your app to continue executing, you should 1) destroy the
// current WebView instance, 2) specify logic for how the app can
// continue executing, and 3) return "true" instead.
return false;
}
I have implemented this method and do not make a distinction between the renderer crashing or being killed by the system. As far as the app is concerned the result is the same - the webview is unusable, and if this method returns false
the app will also be killed.
I maintain a reference to the WebView and two the layout root view in the Fragment (or Activity) that is initialised during the Fragment/Activity creation.
m_homeWebSwipe = v.findViewById(R.id.homeWebViewSwipe);
m_homeWebView = v.findViewById(R.id.homeWebView);
initializeWebView(m_homeWebView);
My WebView is the child of a SwipeRefreshLayout. The layout XML is not critical, but for reference it is:
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/homeWebViewSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/homeWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</WebView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
The key here is the WebView is contained in a Layout container at the root of the view.
On receiving the onRenderProcessGone
event, the goal is to recreate a functional view containing a new WebView (which will have a new renderer process associated with it).
@Override
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
// Renderer was killed or died, recreate the webview
Log.e("HomeWebView", "web content rendering process killed - resetting WebView: " + view.hashCode());
// Only handle our WebView
if (m_homeWebView.equals(view)) {
// Get the parent container of the inflated layout
ViewGroup container = (ViewGroup) m_homeWebSwipe.getParent();
ViewGroup.LayoutParams params = container.getLayoutParams();
// Remove the inflated view from the container and cleanup
// the dead webview specifically (if it is not GC'ed it will cause
// problems later, the next time the renderer dies)
container.removeView(m_homeWebSwipe);
m_homeWebSwipe = null;
m_homeWebView = null;
view.destroy();
// Reinflate the view layout and add it back into the container
View v = getLayoutInflater().inflate(R.layout.home_webview_screen, container, false);
m_homeWebSwipe = v.findViewById(R.id.homeWebViewSwipe);
m_homeWebView = v.findViewById(R.id.homeWebView);
// Initialise webview here, same as when it was originally created
initializeWebView(m_homeWebView);
assert(getActivity() != null);
getActivity().setContentView(v, params);
// reload the displayed page, or load a new page here
reloadPage();
return true; // The app continues executing.
}
return false; // the app is killed
}
You can test the code in the onRenderProcessGone
method by arranging for the WebView to load the URL chrome://crash
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With