Mat*_*adt 73 android android-webview
我有一个使用xml布局的活动,其中嵌入了WebView.我不是在我的活动代码中使用的WebView可言,它是所有在我的XML布局坐在那里,是可见的.
现在,当我完成活动时,我发现我的活动没有从内存中清除.(我通过hprof转储检查).如果我从xml布局中删除WebView,则活动完全清除.
我已经试过了
webView.destroy();
webView = null;
Run Code Online (Sandbox Code Playgroud)
在我的活动的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]
Run Code Online (Sandbox Code Playgroud)
我发现,另一个开发经历了类似的事情,看到菲利佩阿布兰特什的回答上: 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的内存?
Mat*_*adt 54
我从上面的评论和进一步的测试中得出结论,问题是SDK中的一个错误:当通过XML布局创建WebView时,活动作为WebView的上下文传递,而不是应用程序上下文.完成活动后,WebView仍会保留对活动的引用,因此活动不会从内存中删除.我提交了一份错误报告,请参阅上面评论中的链接.
webView = new WebView(getApplicationContext());
Run Code Online (Sandbox Code Playgroud)
请注意,此解决方法仅适用于某些用例,即如果您只需要在Webview中显示html,没有任何href-links或指向对话框的链接等.请参阅下面的注释.
cal*_*er9 32
我对这个方法运气不错:
将FrameLayout作为容器放在xml中,我们称之为web_container.然后以编程方式广告WebView,如上所述.onDestroy,将其从FrameLayout中删除.
假设这是你的xml布局文件中的某个地方,例如layout/your_layout.xml
<FrameLayout
android:id="@+id/web_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Run Code Online (Sandbox Code Playgroud)
然后在向视图膨胀之后,将使用应用程序上下文实例化的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();
}
}
Run Code Online (Sandbox Code Playgroud)
FrameLayout以及layout_width和layout_height也是从它工作的现有项目中任意复制的.我假设另一个ViewGroup可以工作,我确信其他布局尺寸也可以.
此解决方案也适用于RelativeLayout而不是FrameLayout.
emm*_*mby 10
这是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<Activity> activityRef;
public MyWebViewClient( Activity activity ) {
this.activityRef = new WeakReference<Activity>(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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
要使用它,只需在布局中使用NonLeakingWebView替换WebView
<com.mycompany.view.NonLeakingWebView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
...
/>
Run Code Online (Sandbox Code Playgroud)
然后确保NonLeakingWebView.destroy()
从您的activity的onDestroy方法调用.
请注意,此webclient应处理常见情况,但可能不像常规Web客户端那样功能齐全.例如,我没有测试过像flash这样的东西.
基于user1668939在这篇文章(/sf/answers/868609241/)上的答案,这就是我在片段中修复WebView泄漏的方法:
@Override
public void onDetach(){
super.onDetach();
webView.removeAllViews();
webView.destroy();
}
Run Code Online (Sandbox Code Playgroud)
与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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我假设您正在调用WebView字段"webView"(是的,不幸的是,您的WebView引用必须是一个字段).我还没有找到另一种方法,它将独立于字段的名称(除非我遍历所有字段并检查每个字段是否来自WebView类,我不想为性能问题做).