一、Volley框架简介
在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,Google 在2013年的I/O大会 上,发布了Volley。Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。
Volley提供了JsonObjectRequest、JsonArrayRequestStringRequest等Request形式
JsonObjectRequest:返回JSON对象
JsonArrayRequest:返回JsonArray。
StringRequest:返回String,这样可以自己处理数据,更加灵活
另外可以继承自定义Request。
二、VOlley引起的OOM项目中用到Volley下载网络图片,在回调中获取到bitmap做相应地操作。
ImageRequest imageRequest = new ImageRequest(url+"--"+ bmWidth+"x"+bmWidth+".png", new Response.Listener<Bitmap>() { @Override public void onResponse(Bitmap
response) { initViewWithBitmap(response); } }, 0, 0, Bitmap.Config.RGB_565, getImageError); mQueue.add(imageRequest);
起初发现在这个界面操作偶先强关问题,抓到log看下是 outofmemory引起的。
由于该界面用到bitmap的地方比较多,所以在onDestoy()方法中手动释放bitmap。
用内存查看器观察该界面的内存情况,发现每次进入退出后,内存并没有完全释放,大概每次都来6M左右
三、问题分析
工具:MAT内存分析工具
用MAT内存分析工具,找出引起内存没有释放的原因
发现是context没有释放,导致内存泄露,继续分析,找到没有引起GC的保持该引用的地方
找到最短的GC path,发现是Volley中德CacheDispacher保持了context的引用,导致没有引起GC释放内存。
但是volley框架内的东西怎么会引起这种问题?
首先想到在onDestroy中取消掉没有加载完的request。但是没有效果
google 了半天,终于在stackoverflow上找到了答案,大概的意思就是说,初始化RequestQueue的时候尽量使用全局的Context也就是ApplicationContext。
否则的话没有Activity都要维护一个volley请求队列以及分发线程、请求线程和缓存线程。
这样就会导致,在volley框架的内部保持了一个Activity的context引用,也就是说,当我们的Activity的生命周期结束了之后,volley中可能还有没走完的子线程中依然保留该context的引用,导致无法内存回收。
四、解决方法
首先,我们要保持项目中唯一一个Volley的RequestQueue,使用单例模式来实现
public class SingleRequestQueue {
private static RequestQueue mQueue; private SingleRequestQueue(Context context) { mQueue = Volley.newRequestQueue(context); } public static synchronized RequestQueue getRequestQueue(Context context){ if (mQueue == null){ new SingleRequestQueue(context.getApplicationContext()); } return mQueue; } }
在统一一个类中处理网络请求。
另外,看到网上评论说当加载大的网络图片的时候不建议使用Volley,我用的是UniversalImageLoader。
推荐一篇文章讲解Android 中Context。点这里
总结:
之前一直没有遇到context上下文内存泄露的问题,这个问题很好地弥补了这方面的空白
通过这个问题的分析解决,加深了内存泄露的分析思路,同时也巩固了MAT内存分析工具的使用方法。