最後在實現一個無限循環的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傳給別人,要注意內存泄露,具體看我另外一篇博客
這裏還說明另一個問題,就是變量,能不用成員變量就儘可能不要用,否則,一旦內存泄露,會把成員變量的內存也泄露了,我這裏就是成員變量致使的內存泄露