1. 程式人生 > >android記憶體優化之webview

android記憶體優化之webview

在混合型app中它是主角,一切由它呈現,如58同城,趕集網等;在另一些超級app中亦有它的影子,微信,qq,支付寶,沒有一個超級app能少了它,既能展示最新最潮的實時資訊,又能扮演盤踞一方的全功能型網站,與native結合後又能扮演諸如公眾號之內的應用等等,其能力可想而知。

webview在android端的演化可謂曲折,2015年google宣佈不在支援4.4版本一下的webview[1],這意味著目前扔有近四分之一的Android使用者因無法獲得Google的支援而受到安全威脅。對於深度依賴webview的巨頭開始自家的核心及上層sdk以求提升整體穩定性及其效能,騰訊x5核心是比較通用的一種.

目前大致的webview記憶體處理方式分為兩類:

1.獨立的web程序,與主程序隔開

這個方法被運用於類似qq,微信這樣的超級app中,這也是解決任何webview記憶體問題屢試不爽的方法
對於封裝的webactivity,在manifest.xml中設定

<activity android:name=".webview.WebViewActivity" android:launchMode="singleTop" android:process=":remote" android:screenOrientation="unspecified" />

然後在關閉webactivity時銷燬程序

@Overrideprotected void onDestroy() {                
     super.onDestroy(); 
     System.exit(0);
}

關閉瀏覽器後便銷燬整個程序,這樣一般95%的情況下不會造成記憶體洩漏之類的問題,但這就涉及到android程序間通訊,比較不方便處理, 優劣參半,也是可選的一個方案.

2.封裝過的webview

相比系統內建的webview的支援自2005年之後就沒有了,而首推google的chrome。 騰訊的x5webview對h5的相容性與穩定性與安全性逐漸凸顯出來,並自成一系, 下面以使用x5webview為例做說明:

  • 首先使用webview的時候,不在xml裡面宣告,而是直接程式碼new個物件,傳入application context防止activity引用濫用.
webView =  new BridgeWebView(getContext().getApplicationContext());
webFrameLayout.addView(webView, 0);

在使用了這個方式後,基本上90%的webview記憶體洩漏的問題便得以解決.

  • 而在android4.4版本以下,會出現android webview無法自動釋放,如在fragment中,使用ondetach的釋放webview是比較好的時機[2]
public void onDetach() {
    releaseWebViews();
    super.onDetach();
}
public synchronized void releaseWebViews() {
    if(webView != null) {
        try {
            if(webView.getParent() != null) {
                ((ViewGroup) webView.getParent()).removeView(webView);
            }
//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//this is causing the segfault occasionally below 4.2
            webView.destroy();
    //                }
        }catch (IllegalArgumentException e) {
            DLog.p(e);
        }
        RefWatcher refWatcher = FApplication.getRefWatcher();
        refWatcher.watch(webView);
        webView = null;
    }
}

其中webview自身的銷燬程式碼如下:

@Override
public void destroy()
//flushMessageQueue();
    clearCache(true);
    clearFormData();
    clearMatches();
    clearSslPreferences();
    clearDisappearingChildren();
    clearHistory();
    //@Deprecated
    //clearView();
    clearAnimation();
    loadUrl("about:blank");
    removeAllViews();
    freeMemory();
    super.destroy();
}
  • 如果以上的方案還不管用,實時加入反射來清理webview的引用:
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中加入

public void onCreate(BundlesavedInstanceState){
    super.onCreate(savedInstanceState);
    setConfigCallback((WindowManager);
    getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
}
publicvoidonDestroy()
{
    setConfigCallback(null);
    super.onDestroy();
}
  • 對於application 級別的可能的misbehaving callbacks,加入
private static String[] misbehavingClasses = new String[]{
    "com.google.android.gms.ads",
    "com.android.org.chromium.android_webview.AwContents$AwComponentCallbacks",
};
public static boolean isMisbehavingCallBacks(String name){
    for(String s : misbehavingClasses){
        if(name.startsWith(s)){
            return true;
        }
    }
    return false;
}

然後重寫applicaiton類記錄這些callback, 並在適當的時機刪掉:

@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
    super.registerComponentCallbacks(callback);
    ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksRegistered(callback);
}

@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
    ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.onComponentCallbacksUnregistered(callback);
    super.unregisterComponentCallbacks(callback);
}

public void forceUnregisterComponentCallbacks() {
    ComponentCallbacksBehavioralAdjustmentToolIcs.INSTANCE.unregisterAll(this);
}


private static class ComponentCallbacksBehavioralAdjustmentToolIcs {
    private static final String TAG = "componentCallbacks";
    static ComponentCallbacksBehavioralAdjustmentToolIcs INSTANCE = new ComponentCallbacksBehavioralAdjustmentToolIcs();

    private WeakHashMap<ComponentCallbacks, ApplicationErrorReport.CrashInfo> mCallbacks = new WeakHashMap<>();
    private boolean mSuspended = false;

    public void onComponentCallbacksRegistered(ComponentCallbacks callback) {
        Throwable thr = new Throwable("Callback registered here.");
        ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(thr);

        if (FApplication.DEBUG) DLog.w(TAG, "registerComponentCallbacks: " + callback.getClass().getName(), thr);

        if (!mSuspended) {
            if (BugFix.isMisbehavingCallBacks(callback.getClass().getName())) {
                mCallbacks.put(callback, ci);
            }
            // TODO: other classes may still prove to be problematic?  For now, only watch for .gms.ads, since we know those are misbehaving
        } else {
            if (FApplication.DEBUG) DLog.e(TAG, "ComponentCallbacks was registered while tracking is suspended!");
        }
    }

    public void onComponentCallbacksUnregistered(ComponentCallbacks callback) {
        if (!mSuspended) {
            if (FApplication.DEBUG) {
                DLog.i(TAG, "unregisterComponentCallbacks: " + callback, new Throwable());
            }

            mCallbacks.remove(callback);
        }
    }

    public void unregisterAll(Context context) {
        mSuspended = true;
        for (Map.Entry<ComponentCallbacks, ApplicationErrorReport.CrashInfo> entry : mCallbacks.entrySet()) {
            ComponentCallbacks callback = entry.getKey();
            if (callback == null) continue;

            if (FApplication.DEBUG) {
                DLog.w(TAG, "Forcibly unregistering a misbehaving ComponentCallbacks: " + entry.getKey());
                DLog.w(TAG, entry.getValue().stackTrace);
            }

            try {
                context.unregisterComponentCallbacks(entry.getKey());
            } catch (Exception exc) {
                if (FApplication.DEBUG) DLog.e(TAG, "Unable to unregister ComponentCallbacks", exc);
            }
        }

        mCallbacks.clear();
        mSuspended = false;
    }
}
為方便webactivity的debug
  • applicationoncreate裡面,我們加入
private static void enableStrictMode() {
StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
    new StrictMode.ThreadPolicy.Builder()
    .detectAll()
    .penaltyLog();
    StrictMode.VmPolicy.Builder vmPolicyBuilder =
    new StrictMode.VmPolicy.Builder()
    .detectAll()
    .penaltyLog();
    threadPolicyBuilder.penaltyFlashScreen();
    vmPolicyBuilder.setClassInstanceLimit(WebActivity.class, 1);
    StrictMode.setThreadPolicy(threadPolicyBuilder.build());
    StrictMode.setVmPolicy(vmPolicyBuilder.build());
}
  • 在webview中,對於android4.4以上的版本,我們開啟除錯模式
if (FApplication.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WebView.setWebContentsDebuggingEnabled(true);
}
  • 最後不可缺少的是, leakcanary的加入來跟蹤這些消耗記憶體的元件:
    webfragmentwebactivitywebview ondestory後加上
    RefWatcher refWatcher = FApplication.getRefWatcher();
    refWatcher.watch(obj);

總結: 如果你只是簡單地用 webview 做呈現, 使用application context啟動webview已經足夠了,但如果你需要webview來播放視訊,處理彈窗等複雜工作, 新建一個程序來處理會更可靠.

相關推薦

android記憶體優化webview

在混合型app中它是主角,一切由它呈現,如58同城,趕集網等;在另一些超級app中亦有它的影子,微信,qq,支付寶,沒有一個超級app能少了它,既能展示最新最潮的實時資訊,又能扮演盤踞一方的全功能型網站,與native結合後又能扮演諸如公眾號之內的應用等等,其能力可想而知。webview在android端的演

android記憶體優化記憶體分析工具的使用

 anroid記憶體分析工具的使用 一.Eclipse Heap分析記憶體洩露           Android開發中避免不了碰到記憶體洩露問題,這裡先大概講下記憶體洩露的基本概念:記憶體洩露官方的解釋是是用動態儲存分配函式動態開闢的空間,在使用完畢後未釋放,結果導致一直

Android-記憶體優化OOM

Android的記憶體優化是效能優化中很重要的一部分,而避免OOM又是記憶體優化中比較核心的一點,這是一篇關於記憶體優化中如何避免OOM的總結性概要文章,內容大多都是和OOM有關的實踐總結概要。理解錯誤或是偏差的地方,還請多包涵指正,謝謝! (一)Android的記憶體

Android記憶體優化——記憶體洩漏篇

原文地址:http://blog.csdn.net/ys408973279/article/details/50389200 在Android開發中,我們經常會使用到static來修飾我們的成員變數,其本意是為了讓多個物件共用一份空間,節省記憶體,或者是使用單例模式,

Android應用優化記憶體概念

導語 現在的Android智慧手機發展資訊萬變,從一開始的HTC到小米價格戰到現在高階市場份額戰,在軟硬體都發生了翻天覆地的變化。在硬體上記憶體從一開始的一兩百M到現在4G。從軟體上我們從一開始為了實現需求而寫程式碼到現在為了程式碼更健壯、更漂亮而進行不斷優化程式碼。這些都是Andr

Android 效能優化記憶體洩漏檢測以及記憶體優化(中)

Android 記憶體洩漏檢測   通過上篇部落格我們瞭解了 Android JVM/ART 記憶體的相關知識和洩漏的原因,再來歸類一下記憶體洩漏的源頭,這裡我們簡單將其歸為一下三類:自身編碼引起由專案開發人員自身的編碼造成;第三方程式碼引起這裡的第三

Android 效能優化記憶體檢測、卡頓優化、耗電優化、APK瘦身

導語 自2008年智慧時代開始,Android作業系統一路高歌,10年智慧機發展之路,如今 Android 9.0 代號P  都發布了,Android系統性能已經非常流暢了。但是,到了各大廠商手裡,改原始碼自定系統,使得Android原生系統變得魚龍混雜。另外,到了不同層次的

Android 效能優化記憶體洩漏的檢測與修復

在 Android 開發中, 記憶體優化是APP效能優化中很重要的一個部分. 而在記憶體優化中, 最重要的就是修復記憶體洩漏問題. 本文就來介紹一下記憶體洩漏的基本概念以及常用的檢測手段. 1. 什麼是記憶體洩漏 簡單來說, 當一個物件不再被使用時,

Android 效能優化記憶體洩漏檢測以及記憶體優化(下)

Android 記憶體優化   上篇部落格描述瞭如何檢測和處理記憶體洩漏,這種問題從某種意義上講是由於程式碼的錯誤導致的,但是也有一些是程式碼沒有錯誤,但是我們可以通過很多方式去降低記憶體的佔用,使得應用的整體記憶體處於一個健康的水平,下面總結一下記憶

Android效能優化記憶體

Google近期在Udacity上釋出了Android效能優化的線上課程,分別從渲染,運算與記憶體,電量幾個方面介紹瞭如何去優化效能,這些課程是Google之前在Youtube上釋出的Android效能優化典範專題課程的細化與補充。 下面是記憶體篇章的學習筆記,部分內容與前面的效能優化典範有重合,歡

Android 效能優化記憶體優化

在移動作業系統上,通常實體記憶體有限,儘管 Android 的 Dalvik 虛擬機器扮演了常規的垃圾回收的角色,但這並不意味著我們可以忽略 APP 的記憶體分配與釋放,為了 GC 能夠從 APP 中及時回收記憶體,我們在日常的開發中就需要時刻注意記憶體洩露,並在合適的時候來

Android App優化ANR詳解

nic orm rim manage dal private rom syn UNC 引言 背景:Android App優化, 要怎麽做? Android App優化之性能分析工具 Android App優化之提升你的App啟動速度之理論基礎 Android App優化

Android——效能優化SparseArray

相信大家都用過HashMap用來存放鍵值對,最近在專案中使用HashMap的時候發現,有時候 IDE 會提示我這裡的HashMap可以用SparseArray或者SparseIntArray等等來代替。 SparseArray(稀疏陣列).它是Android內部特有的api,標準的jdk是沒有這

Android效能優化較精確的獲取影象顯示到螢幕上的時間

轉載自:http://blog.desmondyao.com/android-show-time/ 這兩天我的包工頭歪龍木·靈魂架構師·王半仙·Yrom給我派了一個活:統計App冷啟動時間。這個任務看上去不難,但是要求統計出來的時間要準,要特別準。 意思就是,我必須要按Activity繪製到

Android應用優化冷啟動優化

前言 事件發生在發包上線的前兩天,在某某雲進行移動測試時,提示冷啟動速度低於平均值的問題,之前自己也曾嘗試過優化,但是發現效果並不是很明顯,作為一個有追求的開發者,趁著有點空閒時間,要好好研究一下冷啟動優化問題。 App的啟動流程 我們可以瞭解一下官方文件《App startup time》對App啟動

Android開發優化的強引用、軟引用、弱引用的使用

本文轉載至:http://www.jianshu.com/p/8488079a939b 引言 早在JDK1.2,Java就把物件的引用分為四種級別,從而使程式能更加靈活的控制物件的生命週期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。 但是平時我們的程式碼中似乎很

Android 記憶體洩漏LeakCanary

導言: 記憶體管理是android開發效能中重要的一環,而leakCanary是Square開源框架,是一個Android記憶體洩露檢測庫,是個優秀的 記憶體洩露檢測工具,通過它大大降低oom的出現,提高app的質量 釋義: 記憶體洩漏:物件在有限生命週期內還持有引用,沒有被回

android電量優化Battery Historian工具使用

前幾天寫了關於androidAPP效能優化總結的文章,還沒有看的話可以看一下,這文章提到了電量優化,android耗電分析所用到的工具battery-historian,這裡做一個總結. 在 Android5.0 以前,在應用中測試電量消耗比較麻煩,也不準確,5.0 之後專門引入了一個獲取裝置上

Android記憶體快取LruCache

LruCache是什麼? LruCache 是least recentlly used 的簡稱,意思是最近最少使用演算法,它可以實現將最近最少使用的物件回收掉,從而保證記憶體不會超出範圍可將LruCache當成一個容器,它就相當於一個map集合。   為什麼使用Lr

Android佈局優化merge標籤詳解

我們都知道View的繪製流程需要經歷measure、layout、draw這個三個過程,如果佈局巢狀層次比較深的話,每一步都需要進行遍歷所有子View進行對應的measure、layout、draw過程,由此就會降低繪製效率,巢狀越多,耗時就越多;其實不光光只會影響view的繪製效率,同