Android: 記一次Android內存泄露

關於內存泄露

內存泄漏也稱做「存儲滲漏」,用動態存儲分配函數動態開闢的空間,在使用完畢後未釋放,結果致使一直佔據該內存單元。直到程序結束。(其實說白了就是該內存空間使用完畢以後未回收)即所謂內存泄漏。(摘自度娘)java

內存泄露了

在一次項目的開發中,代碼的健壯性以及重用性尤其重要;
在不健壯的代碼中,會致使各類各樣的bug,最多見的就是內存泄漏了。有些內存泄露是我的代碼的問題,有些則是系統API帶來的問題;還好咱們有各類各樣的內存檢查工具來幫助咱們。android

<!--more-->git

在一個Android項目中,要使用到一個經常使用的判斷網絡狀態的功能;github

代碼:網絡

public static boolean isNetWorkConnected(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        return netInfo != null && netInfo.isConnected();
    }

...

public class MainActivity extends BaseActivity {
     ...
        if(isNetWorkConnected(MainActivity.this)){
            ... todo:  Network Fine
        }else{
            ... todo: No Network
        }
     ...
}

代碼並不難理解,可使用了LeakCanary檢查以後 發現了內存泄露;ide

* GC ROOT static android.net.ConnectivityManager.sInstance
* references android.net.ConnectivityManager.mContext
* leaks top.itmp.jiandan.ui.MainActivity instance

看着很明顯了, 最終問題 出在ConnectivityManager上,Google以後發現,
github上有些相似的bug: Memory leak in WiFiManager from Android SDK函數

看來是Android getSystemService()的實現中的問題;工具

部分Android源碼實現以下:fetch

//ContextImpl.java
@Override
public Object getSystemService(String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
    return fetcher == null ? null : fetcher.getService(this);
}
 
static {
    registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
            public Object getService(ContextImpl ctx) {
            return AccessibilityManager.getInstance(ctx);
            }});
 
    registerService(CAPTIONING_SERVICE, new ServiceFetcher() {
            public Object getService(ContextImpl ctx) {
            return new CaptioningManager(ctx);
            }});
 
    registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
            public Object createService(ContextImpl ctx) {
            IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
            IAccountManager service = IAccountManager.Stub.asInterface(b);
            return new AccountManager(ctx, service);
            }});
    // ... ...
}

解決辦法

所已在Android開發中, 瞭解甚至熟悉Android系統的源碼實現仍是很重要的;畢竟仍是開源的好。ui

解決辦法: 使用Application Context 替換 Activity context 便可;

if(isNetWorkConnected(getApplicationContext())){
    ...
}

或者:

ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);

便可解決問題。

總結

  1. 不得不說 leakcanary 真是個好東西。

  2. 日常出了問題要多思考,畢竟不少的東西的內部實現決定了外邊運行環境的限制;

  3. 除了ConnectivityManager以外, WifiManager context.getSystemService(Context.CAMERA_SERVICE)等其餘getSystemService() 都有相似內存泄露的狀況。

  4. 學會Google很重要。學會Google很重要。學會Google很重要。重要的事情要說三遍!!!

相關文章
相關標籤/搜索