Created
October 16, 2013 14:58
-
-
Save laaptu/7009064 to your computer and use it in GitHub Desktop.
Avoid memory leak of webview
http://stackoverflow.com/questions/3130654/memory-leak-in-webview
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.lang.ref.WeakReference; | |
import java.lang.reflect.Field; | |
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; | |
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; | |
} | |
} | |
} | |
///OnActivity finish or destroy call | |
@Override | |
public void finish() { | |
webView.destroy(); | |
super.finish(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Iaaptu
I hope everything is going on well with you.
If your app has only 1 webview, this might work; or else, it will leak every Activity with a webview. About 3 years ago, somebody in my team made this change, then the app crashed a lot. finally I found the reason and removed this change.
Please check the code:
Line 209-215
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.5_r1/android/webkit/BrowserFrame.java#BrowserFrame.ConfigCallback
Line 298-301
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.5_r1/android/view/ViewRoot.java#ViewRoot.addConfigCallback%28android.content.ComponentCallbacks%29
and the declaration of 'sConfigCallbacks', it's a static ArrayList that holds references(BrowserFrame.ConfigCallback in the case of WebView).
Every time we click BACK key, and the onDestroy was called in the WebviewActivity, then, BrowserFrame.sConfigCallback was set to null. Then if we go to another WebviewActivity, an WebView is created (so it is with BrowserFrame). If the BrowserFrame.sConfig is null, then a new one would be instantiated and added to a static ArrayList, including the member 'mWindowManger', who has a reference to the WebviewActivity indirectly. So the leak happens.
just FYI