From 2da9db9769f356b92832439184c8f5bcba39c764 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Sat, 20 May 2023 13:03:03 +0200 Subject: [PATCH] fixed android InAppWebView and InAppBrowser dispose functions to correctly dispose resources --- .../in_app_browser/InAppBrowserActivity.java | 32 ++---- .../in_app_webview/FlutterWebView.java | 46 +------- .../webview/in_app_webview/InAppWebView.java | 49 +++++++-- .../InAppWebViewChromeClient.java | 103 +++++++++++------- 4 files changed, 121 insertions(+), 109 deletions(-) diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserActivity.java index 0828c02c..efe8a7ad 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_browser/InAppBrowserActivity.java @@ -13,30 +13,27 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; -import android.webkit.WebChromeClient; import android.webkit.WebView; -import android.webkit.WebViewClient; import android.widget.ProgressBar; +import android.widget.RelativeLayout; import android.widget.SearchView; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController; -import com.pichillilorenzo.flutter_inappwebview.types.Disposable; import com.pichillilorenzo.flutter_inappwebview.R; import com.pichillilorenzo.flutter_inappwebview.Util; +import com.pichillilorenzo.flutter_inappwebview.find_interaction.FindInteractionController; import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshChannelDelegate; -import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView; -import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebViewChromeClient; -import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebViewSettings; -import com.pichillilorenzo.flutter_inappwebview.webview.WebViewChannelDelegate; import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout; import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshSettings; +import com.pichillilorenzo.flutter_inappwebview.types.Disposable; import com.pichillilorenzo.flutter_inappwebview.types.URLRequest; import com.pichillilorenzo.flutter_inappwebview.types.UserScript; +import com.pichillilorenzo.flutter_inappwebview.webview.WebViewChannelDelegate; +import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebView; +import com.pichillilorenzo.flutter_inappwebview.webview.in_app_webview.InAppWebViewSettings; import java.io.IOException; import java.util.ArrayList; @@ -538,19 +535,12 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow manager.plugin.activityPluginBinding != null && webView.inAppWebViewChromeClient != null) { manager.plugin.activityPluginBinding.removeActivityResultListener(webView.inAppWebViewChromeClient); } - if (webView.channelDelegate != null) { - webView.channelDelegate.dispose(); + RelativeLayout containerView = (RelativeLayout) findViewById(R.id.container); + if (containerView != null) { + containerView.removeAllViews(); } - webView.setWebChromeClient(new WebChromeClient()); - webView.setWebViewClient(new WebViewClient() { - public void onPageFinished(WebView view, String url) { - webView.dispose(); - webView.destroy(); - webView = null; - manager = null; - } - }); - webView.loadUrl("about:blank"); + webView.dispose(); + webView = null; finish(); } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java index dde66261..e2f5153c 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/FlutterWebView.java @@ -158,47 +158,13 @@ public class FlutterWebView implements PlatformWebView { @Override public void dispose() { if (keepAliveId == null && webView != null) { - if (webView.channelDelegate != null) { - webView.channelDelegate.dispose(); - } - webView.removeJavascriptInterface(JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) { - WebViewCompat.setWebViewRenderProcessClient(webView, null); - } - webView.setWebChromeClient(new WebChromeClient()); - webView.setWebViewClient(new WebViewClient() { - @Override - public void onPageFinished(WebView view, String url) { - if (webView.inAppWebViewRenderProcessClient != null) { - webView.inAppWebViewRenderProcessClient.dispose(); - } - if (webView.inAppWebViewChromeClient != null) { - webView.inAppWebViewChromeClient.dispose(); - } - if (webView.inAppWebViewClientCompat != null) { - webView.inAppWebViewClientCompat.dispose(); - } - if (webView.inAppWebViewClient != null) { - webView.inAppWebViewClient.dispose(); - } - if (webView.javaScriptBridgeInterface != null) { - webView.javaScriptBridgeInterface.dispose(); - } - if (webView != null) { - webView.dispose(); - webView.destroy(); - webView = null; - } + webView.dispose(); + webView = null; - if (pullToRefreshLayout != null) { - pullToRefreshLayout.dispose(); - pullToRefreshLayout = null; - } - } - }); - WebSettings settings = webView.getSettings(); - settings.setJavaScriptEnabled(false); - webView.loadUrl("about:blank"); + if (pullToRefreshLayout != null) { + pullToRefreshLayout.dispose(); + pullToRefreshLayout = null; + } } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java index 2097a96d..376452eb 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java @@ -45,9 +45,11 @@ import android.webkit.DownloadListener; import android.webkit.URLUtil; import android.webkit.ValueCallback; import android.webkit.WebBackForwardList; +import android.webkit.WebChromeClient; import android.webkit.WebHistoryItem; import android.webkit.WebSettings; import android.webkit.WebStorage; +import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; @@ -80,7 +82,6 @@ import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PluginScriptsU import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PrintJS; import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PromisePolyfillJS; import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobController; -import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobManager; import com.pichillilorenzo.flutter_inappwebview.print_job.PrintJobSettings; import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout; import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld; @@ -107,6 +108,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.regex.Pattern; @@ -2002,6 +2004,23 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie @Override public void dispose() { + if (channelDelegate != null) { + channelDelegate.dispose(); + channelDelegate = null; + } + super.dispose(); + WebSettings settings = getSettings(); + settings.setJavaScriptEnabled(false); + removeJavascriptInterface(JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) { + WebViewCompat.setWebViewRenderProcessClient(this, null); + } + setWebChromeClient(new WebChromeClient()); + setWebViewClient(new WebViewClient() { + public void onPageFinished(WebView view, String url) { + destroy(); + } + }); userContentController.dispose(); if (findInteractionController != null) { findInteractionController.dispose(); @@ -2026,14 +2045,28 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie callAsyncJavaScriptCallbacks.clear(); evaluateJavaScriptContentWorldCallbacks.clear(); inAppBrowserDelegate = null; - inAppWebViewChromeClient = null; - inAppWebViewClientCompat = null; - inAppWebViewClient = null; - javaScriptBridgeInterface = null; - inAppWebViewRenderProcessClient = null; - channelDelegate = null; + if (inAppWebViewRenderProcessClient != null) { + inAppWebViewRenderProcessClient.dispose(); + inAppWebViewRenderProcessClient = null; + } + if (inAppWebViewChromeClient != null) { + inAppWebViewChromeClient.dispose(); + inAppWebViewChromeClient = null; + } + if (inAppWebViewClientCompat != null) { + inAppWebViewClientCompat.dispose(); + inAppWebViewClientCompat = null; + } + if (inAppWebViewClient != null) { + inAppWebViewClient.dispose(); + inAppWebViewClient = null; + } + if (javaScriptBridgeInterface != null) { + javaScriptBridgeInterface.dispose(); + javaScriptBridgeInterface = null; + } plugin = null; - super.dispose(); + loadUrl("about:blank"); } @Override diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java index c974801e..fc58a282 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebViewChromeClient.java @@ -67,7 +67,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import io.flutter.plugin.common.PluginRegistry; @@ -81,6 +83,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR private static final int PICKER = 1; private static final int PICKER_LEGACY = 3; final String DEFAULT_MIME_TYPES = "*/*"; + final Map dialogs = new HashMap(); protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER); @@ -276,6 +279,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onClick(DialogInterface dialog, int which) { result.confirm(); dialog.dismiss(); + dialogs.remove(dialog); } }; @@ -297,10 +301,12 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onCancel(DialogInterface dialog) { result.cancel(); dialog.dismiss(); + dialogs.remove(dialog); } }); AlertDialog alertDialog = alertDialogBuilder.create(); + dialogs.put(alertDialog, result); alertDialog.show(); } @@ -360,6 +366,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onClick(DialogInterface dialog, int which) { result.confirm(); dialog.dismiss(); + dialogs.remove(dialog); } }; DialogInterface.OnClickListener cancelClickListener = new DialogInterface.OnClickListener() { @@ -367,6 +374,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onClick(DialogInterface dialog, int which) { result.cancel(); dialog.dismiss(); + dialogs.remove(dialog); } }; @@ -393,10 +401,12 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onCancel(DialogInterface dialog) { result.cancel(); dialog.dismiss(); + dialogs.remove(dialog); } }); AlertDialog alertDialog = alertDialogBuilder.create(); + dialogs.put(alertDialog, result); alertDialog.show(); } @@ -476,6 +486,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR String text = input.getText().toString(); result.confirm(finalValue != null ? finalValue : text); dialog.dismiss(); + dialogs.remove(dialog); } }; DialogInterface.OnClickListener cancelClickListener = new DialogInterface.OnClickListener() { @@ -483,6 +494,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onClick(DialogInterface dialog, int which) { result.cancel(); dialog.dismiss(); + dialogs.remove(dialog); } }; @@ -509,11 +521,13 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR public void onCancel(DialogInterface dialog) { result.cancel(); dialog.dismiss(); + dialogs.remove(dialog); } }); AlertDialog alertDialog = alertDialogBuilder.create(); alertDialog.setView(layout); + dialogs.put(alertDialog, result); alertDialog.show(); } @@ -567,50 +581,54 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR } public void createBeforeUnloadDialog(String message, final JsResult result, String responseMessage, String confirmButtonTitle, String cancelButtonTitle) { - String alertMessage = (responseMessage != null && !responseMessage.isEmpty()) ? responseMessage : message; - DialogInterface.OnClickListener confirmClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - result.confirm(); - dialog.dismiss(); - } - }; - DialogInterface.OnClickListener cancelClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - result.cancel(); - dialog.dismiss(); - } - }; - - Activity activity = getActivity(); - if (activity == null) { - return; + String alertMessage = (responseMessage != null && !responseMessage.isEmpty()) ? responseMessage : message; + DialogInterface.OnClickListener confirmClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + result.confirm(); + dialog.dismiss(); + dialogs.remove(dialog); } - - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, androidx.appcompat.R.style.Theme_AppCompat_Dialog_Alert); - alertDialogBuilder.setMessage(alertMessage); - if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { - alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener); - } else { - alertDialogBuilder.setPositiveButton(android.R.string.ok, confirmClickListener); + }; + DialogInterface.OnClickListener cancelClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + result.cancel(); + dialog.dismiss(); + dialogs.remove(dialog); } - if (cancelButtonTitle != null && !cancelButtonTitle.isEmpty()) { - alertDialogBuilder.setNegativeButton(cancelButtonTitle, cancelClickListener); - } else { - alertDialogBuilder.setNegativeButton(android.R.string.cancel, cancelClickListener); + }; + + Activity activity = getActivity(); + if (activity == null) { + return; + } + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity, androidx.appcompat.R.style.Theme_AppCompat_Dialog_Alert); + alertDialogBuilder.setMessage(alertMessage); + if (confirmButtonTitle != null && !confirmButtonTitle.isEmpty()) { + alertDialogBuilder.setPositiveButton(confirmButtonTitle, confirmClickListener); + } else { + alertDialogBuilder.setPositiveButton(android.R.string.ok, confirmClickListener); + } + if (cancelButtonTitle != null && !cancelButtonTitle.isEmpty()) { + alertDialogBuilder.setNegativeButton(cancelButtonTitle, cancelClickListener); + } else { + alertDialogBuilder.setNegativeButton(android.R.string.cancel, cancelClickListener); + } + + alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + result.cancel(); + dialog.dismiss(); + dialogs.remove(dialog); } + }); - alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - result.cancel(); - dialog.dismiss(); - } - }); - - AlertDialog alertDialog = alertDialogBuilder.create(); - alertDialog.show(); + AlertDialog alertDialog = alertDialogBuilder.create(); + dialogs.put(alertDialog, result); + alertDialog.show(); } @Override @@ -1296,6 +1314,11 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR } public void dispose() { + for (Map.Entry dialog : dialogs.entrySet()) { + dialog.getValue().cancel(); + dialog.getKey().dismiss(); + } + dialogs.clear(); if (plugin != null && plugin.activityPluginBinding != null) { plugin.activityPluginBinding.removeActivityResultListener(this); }