安卓WebView窗口泄露

5

我在使用WebView的大型项目中遇到了这个问题,但是当我找不到我的代码有什么问题时,我创建了一个只包含两个内容的简单项目:

MainActivity具有以下特定代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    WebView webView = (WebView) findViewById(R.id.webView);
    Uri uri = Uri.parse("android.resource://whatever");
    webView.loadUrl(uri.toString());
}

activity_main.xml 只包含一个布局和一个 Webview。没有别的东西。

现在,Webview 显示无法显示网站的消息,这没关系。它是否显示都无所谓。问题在于:当我选择文本并弹出窗口(复制 | 粘贴),然后点击返回按钮(关闭活动),我会得到以下异常(没有崩溃):

E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fb64bfadb20
E/WindowManager: android.view.WindowLeaked: Activity com.example.***.richwebeditor.MainActivity has leaked window android.widget.PopupWindow$PopupDecorView{ffd5bff V.E...... ........ 0,0-116,58} that was originally added here
      at android.view.ViewRootImpl.<init>(ViewRootImpl.java:368)
      at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
      at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
      at android.widget.PopupWindow.invokePopup(PopupWindow.java:1258)
      at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1032)
      at android.widget.PopupWindow.showAtLocation(PopupWindow.java:995)
      at org.chromium.content.browser.input.PopupTouchHandleDrawable.show(PopupTouchHandleDrawable.java:354)
      at org.chromium.android_webview.AwContents.nativeOnDraw(Native Method)
      at org.chromium.android_webview.AwContents.access$4500(AwContents.java:92)
      at org.chromium.android_webview.AwContents$AwViewMethodsImpl.onDraw(AwContents.java:2731)
      at org.chromium.android_webview.AwContents.onDraw(AwContents.java:1191)
      at com.android.webview.chromium.WebViewChromium.onDraw(WebViewChromium.java:1713)
      at android.webkit.WebView.onDraw(WebView.java:2486)
      at android.view.View.draw(View.java:16178)
      at android.view.View.updateDisplayListIfDirty(View.java:15174)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281)
      at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287)
      at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322)
      at android.view.ViewRootImpl.draw(ViewRootImpl.java:2615)
      at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2434)
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2067)
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
      at android.view.Choreographer.doCallbacks(Choreographer.java:670)
      at android.view.Choreographer.doFrame(Choreographer.java:606)
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:148)
      at android.app.ActivityThread.main(ActivityThread.java:5417)
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fb63de30880
E/WindowManager: android.view.WindowLeaked: Activity com.example.***.richwebeditor.MainActivity has leaked window android.widget.PopupWindow$PopupDecorView{7220dcc V.E...... ........ 0,0-116,58} that was originally added here
      at android.view.ViewRootImpl.<init>(ViewRootImpl.java:368)
      at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
      at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
      at android.widget.PopupWindow.invokePopup(PopupWindow.java:1258)
      at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1032)
      at android.widget.PopupWindow.showAtLocation(PopupWindow.java:995)
      at org.chromium.content.browser.input.PopupTouchHandleDrawable.show(PopupTouchHandleDrawable.java:354)
      at org.chromium.android_webview.AwContents.nativeOnDraw(Native Method)
      at org.chromium.android_webview.AwContents.access$4500(AwContents.java:92)
      at org.chromium.android_webview.AwContents$AwViewMethodsImpl.onDraw(AwContents.java:2731)
      at org.chromium.android_webview.AwContents.onDraw(AwContents.java:1191)
      at com.android.webview.chromium.WebViewChromium.onDraw(WebViewChromium.java:1713)
      at android.webkit.WebView.onDraw(WebView.java:2486)
      at android.view.View.draw(View.java:16178)
      at android.view.View.updateDisplayListIfDirty(View.java:15174)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593)
      at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573)
      at android.view.View.updateDisplayListIfDirty(View.java:15134)
      at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281)
      at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287)
      at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322)
      at android.view.ViewRootImpl.draw(ViewRootImpl.java:2615)
      at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2434)
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2067)
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
      at android.view.Choreographer.doCallbacks(Choreographer.java:670)
      at android.view.Choreographer.doFrame(Choreographer.java:606)
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:148)
      at android.app.ActivityThread.main(ActivityThread.java:5417)
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
E/Surface: getSlotFromBufferLocked: unknown buffer: 0x7fb63de30340

我在三台不同的三星Galaxy设备上(Android 6.1),一个模拟器(7.0)和一台旧的HTC手机(5.0)上进行了测试。在每种情况下都出现了异常。

有人遇到过这个问题吗?这是Android webview的一个bug吗?有解决方法吗?

编辑: 参考屏幕截图,关于我所说的弹出窗口:

Imgur1

Imgur2


当你完成警告或活动时,请使用dismiss关闭它/对话框.dismiss(); - Charuක
但是这不是我的对话框。它是一个系统弹出窗口,每当用户选择文本复制时都会显示。我无法访问它。 - dabu
你在清单文件中添加了网络权限吗? <uses-permission android:name="android.permission.INTERNET" /> 如果你的应用程序需要使用网络套接字,那么你的应用程序需要获得使用它们的权限。 - Charuක
是的,但那有什么关系吗?我可以很好地显示网站,问题在于复制弹出窗口没有从 Webview 中删除,导致窗口泄漏。当然,在点击“返回”按钮后,弹出窗口会消失,但看起来它仍然引用了现在已经销毁的 Webview。如果您愿意,您可以自己检查一下,看看是否遇到了相同的异常(我认为是这样,因为我已经在许多不同的设备上测试过,并且使用这样基本的代码,我看不出任何错误)。 - dabu
这很重要,因为如果没有互联网,你怎么通过你的应用程序加载一个URL呢?你能添加一张你的“复制弹出窗口”的图片吗?这样我就可以帮助你了。 - Charuක
正如您所看到的,无论我能否显示网站,当我在屏幕上看到的那一刻按下返回按钮时,都会出现顶部帖子中显示的异常。 - dabu
3个回答

2
看起来解决这个问题的好方法是调用
webView.destroy()

完成活动时,我相信有更好的解决方案,并将暂时保留此线程未解决,以便可能有经验的人分享处理方法。

0
也许你应该在结束 Activity 前释放 WebView 使用的资源,尝试在 onDestroy() 中添加 webView.loadUrl("about:blank");

0
如果您想从 Web 视图返回,请在该活动中使用以下内容。
@Override
public void onBackPressed() {
    if (webView.canGoBack()) {
        webView.goBack();
    } else {
        super.onBackPressed(); // maybe you can even change this as needed
    }
}

-创建一个活动。

-生成一个工作线程,该线程具有对该活动的引用。 - 活动以某种方式关闭/完成(很可能是由于用户交互的结果),系统尝试收集其资源。

-以某种方式,该工作线程仍在运行,并且它持有对活动的引用...因此活动已经“泄漏”,系统无法自由地回收它,而不会以潜在危险的方式更改/破坏您的应用程序状态。

-在Android中,活动是重量级对象,(激进的)系统资源管理器需要能够重新获取它们,以确保用户获得流畅和高效的体验。因此,这种“泄漏”被认为是一种错误。

-在这些情况下,在执行操作之前检查引用是否仍然存在


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接