当前位置:  开发笔记 > 编程语言 > 正文

WebView中的内存泄漏

如何解决《WebView中的内存泄漏》经验,为你挑选了4个好方法。

我有一个使用xml布局的活动,其中嵌入了WebView.我不是在我的活动代码中使用的WebView可言,它是所有在我的XML布局坐在那里,是可见的.

现在,当我完成活动时,我发现我的活动没有从内存中清除.(我通过hprof转储检查).如果我从xml布局中删除WebView,则活动完全清除.

我已经试过了

webView.destroy();
webView = null;

在我的活动的onDestroy()中,但这没有多大帮助.

在我的HPROF转储,我的活动(名为"浏览器")有如下剩余GC根(已调用之后destroy()就可以了):

com.myapp.android.activity.browser.Browser
  - mContext of android.webkit.JWebCoreJavaBridge
    - sJavaBridge of android.webkit.BrowserFrame [Class]
  - mContext of android.webkit.PluginManager
    - mInstance of android.webkit.PluginManager [Class]  

我发现,另一个开发经历了类似的事情,看到菲利佩阿布兰特什的回答上: http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/

确实是一个非常有趣的帖子 最近我在Android应用程序上对内存泄漏进行故障排除非常困难.最终事实证明,我的XML布局包括,即使不使用的WebView组件,是防止内存被G-收集屏幕转/应用程序重新启动后,...这是当前实现的一个bug,或者是有什么特定于使用WebView时需要执行的操作

现在,遗憾的是,博客或邮件列表中尚未对此问题做出回复.因此,我想知道,是在SDK中的错误(也许类似的MapView错误报道http://code.google.com/p/android/issues/detail?id=2181)或如何完全得到活动嵌入了webview的内存



1> Mathias Conr..:

我从上面的评论和进一步的测试中得出结论,问题是SDK中的一个错误:当通过XML布局创建WebView时,活动作为WebView的上下文传递,而不是应用程序上下文.完成活动后,WebView仍会保留对活动的引用,因此活动不会从内存中删除.我提交了一份错误报告,请参阅上面评论中的链接.

webView = new WebView(getApplicationContext());

请注意,此解决方法仅适用于某些用例,即如果您只需要在Webview中显示html,没有任何href-links或指向对话框的链接等.请参阅下面的注释.


请注意,使用应用程序上下文意味着您将无法单击Web视图中的链接,因为这样做会导致崩溃:"从Activity上下文外部调用startActivity()需要FLAG_ACTIVITY_NEW_TASK标志.这是否真的你想要什么?"
此外,只要webview尝试创建对话框(例如,记住密码等),webview就会因为期望活动上下文而崩溃.
这个bug在android 5.0 lilpops中仍然有效.所以我们需要记住,我们仍然需要解决这个bug问题.

2> caller9..:

我对这个方法运气不错:

将FrameLayout作为容器放在xml中,我们称之为web_container.然后以编程方式广告WebView,如上所述.onDestroy,将其从FrameLayout中删除.

假设这是你的xml布局文件中的某个地方,例如layout/your_layout.xml


然后在向视图膨胀之后,将使用应用程序上下文实例化的WebView添加到FrameLayout.onDestroy,调用webview的destroy方法并从视图层次结构中删除它,否则你会泄漏.

public class TestActivity extends Activity {
    private FrameLayout mWebContainer;
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.your_layout);

        mWebContainer = (FrameLayout) findViewById(R.id.web_container);
        mWebView = new WebView(getApplicationContext());
        mWebContainer.addView(mWebView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mWebContainer.removeAllViews();
        mWebView.destroy();
    }
}

FrameLayout以及layout_width和layout_height也是从它工作的现有项目中任意复制的.我假设另一个ViewGroup可以工作,我确信其他布局尺寸也可以.

此解决方案也适用于RelativeLayout而不是FrameLayout.



3> emmby..:

这是WebView的一个子类,它使用上面的hack来无缝地避免内存泄漏:

package com.mycompany.view;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.AttributeSet;
import android.webkit.WebView;
import android.webkit.WebViewClient;

/**
 * see http://stackoverflow.com/questions/3130654/memory-leak-in-webview and http://code.google.com/p/android/issues/detail?id=9375
 * Note that the bug does NOT appear to be fixed in android 2.2 as romain claims
 *
 * Also, you must call {@link #destroy()} from your activity's onDestroy method.
 */
public class NonLeakingWebView extends WebView {
    private static Field sConfigCallback;

    static {
        try {
            sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback");
            sConfigCallback.setAccessible(true);
        } catch (Exception e) {
            // ignored
        }

    }


    public NonLeakingWebView(Context context) {
        super(context.getApplicationContext());
        setWebViewClient( new MyWebViewClient((Activity)context) );
    }

    public NonLeakingWebView(Context context, AttributeSet attrs) {
        super(context.getApplicationContext(), attrs);
        setWebViewClient(new MyWebViewClient((Activity)context));
    }

    public NonLeakingWebView(Context context, AttributeSet attrs, int defStyle) {
        super(context.getApplicationContext(), attrs, defStyle);
        setWebViewClient(new MyWebViewClient((Activity)context));
    }

    @Override
    public void destroy() {
        super.destroy();

        try {
            if( sConfigCallback!=null )
                sConfigCallback.set(null, null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    protected static class MyWebViewClient extends WebViewClient {
        protected WeakReference activityRef;

        public MyWebViewClient( Activity activity ) {
            this.activityRef = new WeakReference(activity);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            try {
                final Activity activity = activityRef.get();
                if( activity!=null )
                    activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
            }catch( RuntimeException ignored ) {
                // ignore any url parsing exceptions
            }
            return true;
        }
    }
}

要使用它,只需在布局中使用NonLeakingWebView替换WebView

                    

然后确保NonLeakingWebView.destroy()从您的activity的onDestroy方法调用.

请注意,此webclient应处理常见情况,但可能不像常规Web客户端那样功能齐全.例如,我没有测试过像flash这样的东西.



4> Tiago..:

基于user1668939在这篇文章(/sf/ask/17360801/)上的答案,这就是我在片段中修复WebView泄漏的方法:

@Override
public void onDetach(){

    super.onDetach();

    webView.removeAllViews();
    webView.destroy();
}

与user1668939的答案的区别在于我没有使用任何占位符.只需在WebvView引用上调用removeAllViews()就可以了.

##更新##

如果您像我一样并且在几个片段中包含WebView(并且您不想在所有片段上重复上述代码),则可以使用反射来解决它.只需让你的片段扩展这个:

public class FragmentWebViewLeakFree extends Fragment{

    @Override
    public void onDetach(){

        super.onDetach();

        try {
            Field fieldWebView = this.getClass().getDeclaredField("webView");
            fieldWebView.setAccessible(true);
            WebView webView = (WebView) fieldWebView.get(this);
            webView.removeAllViews();
            webView.destroy();

        }catch (NoSuchFieldException e) {
            e.printStackTrace();

        }catch (IllegalArgumentException e) {
            e.printStackTrace();

        }catch (IllegalAccessException e) {
            e.printStackTrace();

        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

我假设您正在调用WebView字段"webView"(是的,不幸的是,您的WebView引用必须是一个字段).我还没有找到另一种方法,它将独立于字段的名称(除非我遍历所有字段并检查每个字段是否来自WebView类,我不想为性能问题做).

推荐阅读
linjiabin43
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有