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很重要。重要的事情要说三遍!!!

相关文章
相关标签/搜索