package com.hkm.ezwebview.webviewleakfix; import android.content.Context; import android.support.v7.app.AppCompatActivity; import android.util.AttributeSet; import android.util.Log; import android.webkit.WebView; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 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. * Author Heskeyo Kam */ public class NonLeakingWebView<T extends PreventLeakClient> extends WebView { private static Field sConfigCallback; private OnScrollChangedCallback mOnScrollChangedCallback; static { try { sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback"); sConfigCallback.setAccessible(true); } catch (Exception e) { // ignored } } public void setWebViewClient(Class<T> client, AppCompatActivity context) { try { Class[] cArg = new Class[1]; //Our constructor has 3 arguments cArg[0] = AppCompatActivity.class; //First argument is of *object* type Long T clientInstance = (T) client.getDeclaredConstructor(cArg).newInstance((AppCompatActivity) context); super.setWebViewClient(clientInstance); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } public NonLeakingWebView(Context context, Class<T> client) { super(context.getApplicationContext()); // super.setWebViewClient(new HBClient(this, b)); } public NonLeakingWebView(Context context, AttributeSet attrs) { super(context.getApplicationContext(), attrs); // super.setWebViewClient(new HBClient(this, b)); } public NonLeakingWebView(Context context, AttributeSet attrs, int defStyle) { super(context.getApplicationContext(), attrs, defStyle); // super.setWebViewClient(new HBClient(this, b)); } //for android 4.1+ private void enable_x_domain_41_after() { try { Field webviewclassic_field = WebView.class.getDeclaredField("mProvider"); webviewclassic_field.setAccessible(true); Object webviewclassic = webviewclassic_field.get(this); Field webviewcore_field = webviewclassic.getClass().getDeclaredField("mWebViewCore"); webviewcore_field.setAccessible(true); Object mWebViewCore = webviewcore_field.get(webviewclassic); Field nativeclass_field = webviewclassic.getClass().getDeclaredField("mNativeClass"); nativeclass_field.setAccessible(true); Object mNativeClass = nativeclass_field.get(webviewclassic); Method method = mWebViewCore.getClass().getDeclaredMethod("nativeRegisterURLSchemeAsLocal", new Class[]{int.class, String.class}); method.setAccessible(true); method.invoke(mWebViewCore, mNativeClass, "http"); method.invoke(mWebViewCore, mNativeClass, "https"); } catch (Exception e) { Log.d("wokao", "enablecrossdomain error"); e.printStackTrace(); } } private void enable_x_domain_41_before() { try { Field field = WebView.class.getDeclaredField("mWebViewCore"); field.setAccessible(true); Object webviewcore = field.get(this); Method method = webviewcore.getClass().getDeclaredMethod("nativeRegisterURLSchemeAsLocal", String.class); method.setAccessible(true); method.invoke(webviewcore, "http"); method.invoke(webviewcore, "https"); } catch (Exception e) { Log.d("wokao", "enablecrossdomain error"); e.printStackTrace(); } } /** * http://stackoverflow.com/questions/22479677/android-webview-access-control-allow-origin */ public void enablecrossdomain_js() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { enable_x_domain_41_after(); } else { enable_x_domain_41_before(); } } @Override public void destroy() { super.destroy(); try { if (sConfigCallback != null) sConfigCallback.set(null, null); } catch (Exception e) { throw new RuntimeException(e); } } @Override protected void onScrollChanged(final int l, final int t, final int oldl, final int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mOnScrollChangedCallback != null) mOnScrollChangedCallback.onScroll(l, t); } public OnScrollChangedCallback getOnScrollChangedCallback() { return mOnScrollChangedCallback; } public void setOnScrollChangedCallback(final OnScrollChangedCallback onScrollChangedCallback) { mOnScrollChangedCallback = onScrollChangedCallback; } /** * Impliment in the activity/fragment/view that you want to listen to the webview */ public static interface OnScrollChangedCallback { void onScroll(int l, int t); } /** * Pauses any extra processing associated with this WebView and its * associated DOM, plugins, JavaScript etc. For example, if this WebView is * taken offscreen, this could be called to reduce unnecessary CPU or * network traffic. When this WebView is again "active", call onResume(). * Note that this differs from pauseTimers(), which affects all WebViews. */ @Override public void onPause() { super.onPause(); } /** * Resumes a WebView after a previous call to onPause(). */ @Override public void onResume() { super.onResume(); } }