關(guān)于 Android WebView 的內(nèi)存泄露問(wèn)題
在這次開(kāi)發(fā)過(guò)程中,需要用到webview展示一些界面,但是加載的頁(yè)面如果有很多圖片就會(huì)發(fā)現(xiàn)內(nèi)存占用暴漲,并且在退出該界面后,即使在包含該webview的Activity的destroy()方法中,使用webview.destroy();webview=null;對(duì)內(nèi)存占回收用還是沒(méi)有任何效果。有人說(shuō),一旦在你的xml布局中引用了webview甚至沒(méi)有使用過(guò),都會(huì)阻礙重新進(jìn)入Application之后對(duì)內(nèi)存的gc。包括使用MapView有時(shí)一會(huì)引發(fā)OOM,幾經(jīng)周折在網(wǎng)上看到各種解決辦法,在這里跟大家分享一下。但是到目前為止還沒(méi)有找到根本的解決辦法,網(wǎng)上也有說(shuō)是sdk的bug。但是不管怎么樣,我們還是需要使用的。
要使用WebView不造成內(nèi)存泄漏,首先應(yīng)該做的就是不能在xml中定義webview節(jié)點(diǎn),而是在需要的時(shí)候動(dòng)態(tài)生成。即:可以在使用WebView的地方放置一個(gè)LinearLayout類(lèi)似ViewGroup的節(jié)點(diǎn),然后在要使用WebView的時(shí)候,動(dòng)態(tài)生成即:
WebView mWebView = new WebView(getApplicationgContext()); LinearLayout mll= findViewById(R.id.xxx); mll.addView(mWebView);
, 然后一定要在onDestroy()方法中顯式的調(diào)用
protected void onDestroy() { super.onDestroy(); mWebView.removeAllViews(); mWebView.destroy()}
;注意:new WebView(getApplicationgContext());必須傳入ApplicationContext如果傳入Activity的Context的話,對(duì)內(nèi)存的引用會(huì)一直被保持著。有人用這個(gè)方法解決了當(dāng)Activity被消除后依然保持引用的問(wèn)題。但是你會(huì)發(fā)現(xiàn),如果你需要在WebView中打開(kāi)鏈接或者你打開(kāi)的頁(yè)面帶有flash,獲得你的WebView想彈出一個(gè)dialog,都會(huì)導(dǎo)致從ApplicationContext到ActivityContext的強(qiáng)制類(lèi)型轉(zhuǎn)換錯(cuò)誤,從而導(dǎo)致你應(yīng)用崩潰。這是因?yàn)樵诩虞dflash的時(shí)候,系統(tǒng)會(huì)首先把你的WebView作為父控件,然后在該控件上繪制flash,他想找一個(gè)Activity的Context來(lái)繪制他,但是你傳入的是ApplicationContext。后果,你可以曉得了哈。
于是大牛們就Activity銷(xiāo)毀后還保持引用這個(gè)問(wèn)題,提供了另一種解決辦法:既然你不能給我刪除引用,那么我就自己來(lái)吧。于是下面的這種方法誕生了:
(作者說(shuō)這個(gè)方法是依賴(lài)android.webkit implementation有可能在最近的版本中失敗)
public void setConfigCallback(WindowManager windowManager) { try {Field field = WebView.class.getDeclaredField('mWebViewCore');field = field.getType().getDeclaredField('mBrowserFrame');field = field.getType().getDeclaredField('sConfigCallback');field.setAccessible(true);Object configCallback = field.get(null);if (null == configCallback) { return;}field = field.getType().getDeclaredField('mWindowManager');field.setAccessible(true);field.set(configCallback, windowManager); } catch(Exception e) { }}
然后在Activity中調(diào)用上面的方法:
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setConfigCallback((WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE));}public void onDestroy() { setConfigCallback(null); super.onDestroy();}
該反射方法在我的實(shí)驗(yàn)中(2.3.6)確實(shí)有些用處,在應(yīng)用內(nèi)存占用到70M左右的時(shí)候會(huì)明顯釋放到50M或者60M然后的釋放就有些緩慢,其實(shí)就是看不出來(lái)了。之前在沒(méi)使用該方法的時(shí)候可能達(dá)到120M。
但是!!!我們的應(yīng)用要求占用內(nèi)存更低啊,這腫么拌?涼拌么?No。在各種糾結(jié)之后,終于找到了終極解決辦法!!!該辦法適用于我們的需求,在退出WebView的界面之后,迅速回收內(nèi)存。要問(wèn)這個(gè)方法是什么,不要9999,不要8999,只要你仔細(xì)看好下面一句話:那就是為加載WebView的界面開(kāi)啟新進(jìn)程,在該頁(yè)面退出之后關(guān)閉這個(gè)進(jìn)程。
這一點(diǎn)說(shuō)了之后,你懂了吧?
但是在這個(gè)其中,殺死自己進(jìn)程的時(shí)候又遇到了問(wèn)題,網(wǎng)上介紹的各種方法都不好使,
killBackgroundProcesses(getPackageName());各種不好用,最后使用System.exit(0);直接退出虛擬機(jī)(Android為每一個(gè)進(jìn)程創(chuàng)建一個(gè)虛擬機(jī)的)。這個(gè)肯定不用糾結(jié)了,一旦退出,內(nèi)存里面釋放。聽(tīng)濤哥說(shuō)QQ也是這么做。
最后英雄要問(wèn)出處,附上大牛解說(shuō)引起該問(wèn)題的出處
這個(gè)泄漏出現(xiàn)在external/webkit/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp.中。具體我自己真心沒(méi)有深入研究。大家有興趣的話,可以看看哈。
--- a/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp+++ b/Source/WebKit/android/WebCoreSupport/UrlInterceptResponse.cpp@@ -63,10 +63,10 @@ public: JNIEnv* env = JSC::Bindings::getJNIEnv(); // Initialize our read buffer to the capacity of out. if (!m_buffer) {- m_buffer = env->NewByteArray(out->capacity());- m_buffer = (jbyteArray) env->NewGlobalRef(m_buffer);+ ScopedLocalRef<jbyteArray> buffer_local(env, env->NewByteArray(out->capacity()));+ m_buffer = static_cast<jbyteArray>(env->NewGlobalRef(buffer_local.get())); } int size = (int) env->CallIntMethod(m_inputStream, m_read, m_buffer); if (checkException(env) || size < 0) return; // Copy from m_buffer to out.
還有一個(gè)問(wèn)題要說(shuō)的,也是在WebView使用的時(shí)候出現(xiàn)的問(wèn)題:WebView中包含一個(gè)ZoomButtonsController,當(dāng)使用web.getSettings().setBuiltInZoomControls(true);啟用該設(shè)置后,用戶(hù)一旦觸摸屏幕,就會(huì)出現(xiàn)縮放控制圖標(biāo)。這個(gè)圖標(biāo)過(guò)上幾秒會(huì)自動(dòng)消失,但在3.0系統(tǒng)以上上,如果圖標(biāo)自動(dòng)消失前退出當(dāng)前Activity的話,就會(huì)發(fā)生ZoomButton找不到依附的Window而造成程序崩潰,解決辦法很簡(jiǎn)單就是在Activity的ondestory方法中調(diào)用web.setVisibility(View.GONE);方法,手動(dòng)將其隱藏,就不會(huì)崩潰了。在3.0一下系統(tǒng)上不會(huì)出現(xiàn)該崩潰問(wèn)題,真是各種崩潰,防不勝防啊!
最后還有內(nèi)存泄漏的一些個(gè)建議:
In summary, to avoid context-related memory leaks, remember the following:
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)Try using the context-application instead of a context-activityAvoid non-static inner classes in an activity if you don’t control their life cycle, use a static inner class and make a weak reference to the activity insideAnd remember that a garbage collector is not an insurance against memory leaks. Last but not least, we try to make such leaks harder to make happen whenever we can.
來(lái)自:http://my.oschina.net/zhibuji/blog/100580
相關(guān)文章:
1. Python作用域與名字空間原理詳解2. 利用ajax+php實(shí)現(xiàn)商品價(jià)格計(jì)算3. Android 項(xiàng)目正式簽名打包教程分享4. Android簽名文件轉(zhuǎn)化為pk8和pem的實(shí)現(xiàn)5. .NET SkiaSharp 生成二維碼驗(yàn)證碼及指定區(qū)域截取方法實(shí)現(xiàn)6. PHP字符串前后字符或空格刪除方法介紹7. layui Ajax請(qǐng)求給下拉框賦值的實(shí)例8. xml中的空格之完全解說(shuō)9. PHP數(shù)據(jù)庫(kù)抽象層之PDO(三)——事務(wù)與自動(dòng)提交10. chat.asp聊天程序的編寫(xiě)方法
