最后在实现一个无限循环的ViewPager,展现图片,功能实现了,可是运行一段时间以后会挂掉。html
多亏了AndroidStudio的Memory Monitor,发现了内存一直在增加。java
怎么触发gc内存都不会减小,肯定了内存泄露了,可是不知哪里出问题了。数据库
一时想到的排查内存泄露的工具,就是MAT了,可是没找到AndroidStudio的MAT插件。缓存
只能先把java heap dump出来先,以下图所示app
dump出来以后,hprof文件会保存在项目下captures目录,以前一直不知到,找了好久。。。eclipse
若是切换到Captures这个tab,是能够直接看到HeapSnapshot的,以下图所示ide
可是这个hprof文件mat不认,须要转换一下,点击hprof文件右键,转成标准.hprof文件便可工具
而后到eclipse用mat插件打开,若是没有安装mat插件请自行搜索解决this
window->open perspective->memory analysisurl
在memory analysis界面下
File->Open Heap Dump->选中上面Android Studio转换以后的.hprof文件便可
在OverView下面点击Top Consumers, 以下图。byte占了大头,内存基本都给它用了
byte占用这么多内存,一想到的就是bitmap没被释放了。
回去研究个人代码
PagerAdapter代码以下
1 public class SlidePicPagerAdapter extends PagerAdapter { 2 3 private List<SlidePicModel> mItems; 4 private int itemSize; 5 6 public void setItems(List<SlidePicModel> items) { 7 mItems = items; 8 itemSize = items.size(); 9 } 10 11 @Override 12 public int getCount() { 13 return ListUtils.isEmpty(mItems) ? 0 : Integer.MAX_VALUE; 14 } 15 16 @Override 17 public boolean isViewFromObject(View view, Object object) { 18 return view == object; 19 } 20 21 @Override 22 public Object instantiateItem(ViewGroup container, int position) { 23 Context context = container.getContext(); 24 SlidePicModel item = mItems.get(getCurPos(position)); 25 ImageView iv = item.getImageView(); 26 if(iv == null){ 27 iv = new ImageView(context); 28 item.setImageView(iv); 29 } 30 final String imgUrl = ImageUrlExtends.getImageUrl(item.getUrl()); 31 Picasso.with(context).load(imgUrl).into(iv); 32 container.addView(iv); 33 return iv; 34 } 35 36 @Override 37 public void destroyItem(ViewGroup container, int position, Object object) { 38 View imageView = (View) object; 39 container.removeView(imageView); 40 } 41 42 43 private int getCurPos(int pos){ 44 return pos % mItems.size(); 45 } 46 }
我用的是Picasso去加载图片。首先把picasso加载图片给屏蔽了,发现内存正常了,擦,觉得我发现了个bug,先去给Picasso提Bug去呢。
再看看我代码,第28行,我把ImageView给缓存到数据源SlidePicModel的成员变量里了。
SlidePicModel代码以下
1 public class SlidePicModel { 2 3 private ImageView imageView; 4 private String url; 5 6 public SlidePicModel(String url) { 7 this.url = url; 8 } 9 10 public ImageView getImageView() { 11 return imageView; 12 } 13 14 public void setImageView(ImageView imageView) { 15 this.imageView = imageView; 16 } 17 18 public String getUrl() { 19 return url; 20 } 21 22 public void setUrl(String url) { 23 this.url = url; 24 } 25 }
要想一想,我这个数据源mItems里面表明好几百张图片,而个人应用是无限循环显示图片,也便是个人数据库mItems是不会回收的。
原本显示完图片,ImageView是要被回收的,可是,却被我数据源里面的model引用着,几百张图片的bitmap被引用着,不能回收,app确定内存溢出。
解决方法以下,把ImageView这个成员变量设置成弱引用,当内存不足时,ImageView能够被内存回收。问题解决,代码以下
1 public class SlidePicModel { 2 3 4 private String url; 5 private WeakReference<ImageView> mImageViewRef; 6 public SlidePicModel(String url) { 7 this.url = url; 8 } 9 10 public ImageView getImageView() { 11 return mImageViewRef == null ? null : mImageViewRef.get(); 12 } 13 14 public void setImageView(ImageView imageView) { 15 // this.imageView = imageView; 16 mImageViewRef = new WeakReference<ImageView>(imageView); 17 } 18 19 public String getUrl() { 20 return url; 21 } 22 23 public void setUrl(String url) { 24 this.url = url; 25 } 26 }
内存回收正常。图片以下
固然了,更好的办法就是,这个ImageView成员变量彻底没有存在的必要,可是当时一时把代码写错,出恰好让我研究了一下内存泄露,对内存泄露有更深的认识。
总结:
以前就有看到一些文章在建议,不要把Activity当成静态成员变量。
其实从我上面出现的问题里就能够发现同样的道理。上面是由于数据源一直存在,没有被内存回收,因此引用的ImageView也没有被回收,致使内存溢出。
同理,若是把Activity当成静态成员变量。那么它的生命周期就会和app的生命周期同样,Activity里所引用的对象都不会被释放,即便activity已经结束了,这就会致使内存举出。
使用context的时候,能使用ApplicationContext就使用AplicationContext,若是把Activity当成context传给别人,要注意内存泄露,具体看我另外一篇博客
这里还说明另一个问题,就是变量,能不用成员变量就尽可能不要用,否则,一旦内存泄露,会把成员变量的内存也泄露了,我这里就是成员变量致使的内存泄露