【Android】实战开发之ViewPager图片回收处理内存溢出完美解决方案(含Fragment)
在Android实战开发中,ViewPager使用广泛,但使用ViewPager加载多个图片容易出现内存溢出的问题,解决此类内存溢出,需要主要注意以下2点:
1、是否进行过图片压缩处理;
Options opts=new Options();
opts.inJustDecodeBounds=true;
BitmapFactory.decodeResource(activity.getResources(), imgs[position], opts);
DisplayMetrics outMetrics=new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
int x=opts.outWidth/outMetrics.widthPixels;
int y=opts.outHeight/outMetrics.heightPixels;
if(x>y&&x>1){
opts.inSampleSize=x;
}else if(y>x&&y>1){
opts.inSampleSize=y;
}
opts.inJustDecodeBounds=false;
Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), imgs[position], opts);
2、是否进行过图片回收处理。
下面针对ViewPager加载多个图片和ViewPager+Fragment加载多个图片两种方式分别说明如何利用图片回收解决内存溢出、内存泄露的完美解决方案。
一、ViewPager+View方式加载图片处理图片回收
1、首先,需要在ViewPager适配器PagerAdapter中重写2个方法destroyItem和instantiateItem,在这2个方法中主要实现如下几个功能:
@Override
public void destroyItem(View container, int position, Object object) {
//TODO 回收图片
//移除页面
((ViewPager)container).removeView(imageView);
}
@Override
public Object instantiateItem(View container, int position) {
//TODO 图片压缩
//TODO 加载图片
//TODO 加载页面
((ViewPager)container).addView(imageView);
return viewList.get(position);
}
2、然后,再在destroyItem方法中,先将使用完的图片引用释放掉,这里要注意用什么方法设置图片,就需要用对应的方法释放图片,比如:
⑴Drawable:
设置图片
imageView.setImageDrawable(drawable);
或
imageView.setImageResource(R.drawable.icon);
释放图片
imageView.setImageDrawable(null);
⑵Bitmap:
设置图片
imageView.setImageBitmap(bitmap);
释放图片
imageView.setImageBitmap(null);
⑶Background:
设置图片
imageView.setBackgroundDrawable(drawable);
或
imageView.setBackgroundResource(R.drawable.icon);
释放图片
imageView.setBackgroundDrawable(null);
3、最后,在destroyItem方法中,进行释放图片资源的操作,代码如下,调用其方法即可:
public void releaseImageViewResouce(ImageView imageView) {
if (imageView == null) return;
Drawable drawable = imageView.getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap=null;
}
}
System.gc();
}
4、PagerAdapter中的完整代码:
public class MyPagerAdapter extends PagerAdapter{
private Activity activity;
private ArrayList<ImageView> viewList;
private int[] imgs;
public MyPagerAdapter (Activity activity,ArrayList<ImageView> viewList, int[] imgs){
this.activity=activity;
this.viewList=viewList;
this.imgs=imgs;
}
@Override
public void destroyItem(View container, int position, Object object) {
//回收图片
ImageView imageView = viewList.get(position);
imageView.setImageBitmap(null);
releaseImageViewResouce(imageView);
//移除页面
((ViewPager)container).removeView(imageView);
}
@Override
public Object instantiateItem(View container, int position) {
//图片压缩
Options opts=new Options();
opts.inJustDecodeBounds=true;
BitmapFactory.decodeResource(activity.getResources(), imgs[position], opts);
DisplayMetrics outMetrics=new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
int x=opts.outWidth/outMetrics.widthPixels;
int y=opts.outHeight/outMetrics.heightPixels;
if(x>y&&x>1){
opts.inSampleSize=x;
}else if(y>x&&y>1){
opts.inSampleSize=y;
}
opts.inJustDecodeBounds=false;
Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), imgs[position], opts);
//加载图片
ImageView imageView = viewList.get(position);
imageView.setImageBitmap(bitmap);
//加载页面
((ViewPager)container).addView(imageView);
return viewList.get(position);
}
@Override
public int getCount() {
return viewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
}
/**
* 释放图片资源的方法
* @param imageView
*/
public void releaseImageViewResouce(ImageView imageView) {
if (imageView == null) return;
Drawable drawable = imageView.getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap=null;
}
}
System.gc();
}
}
二、ViewPager+Fragment方式加载图片处理图片回收
1、在Fragment中,进行图片压缩、加载图片(有必要时可以使用缓加载)操作,方法同ViewPager+View方式。
2、在ViewPager+Fragment适配器FragmentPagerAdapter中,重写destroyItem方法,并进行图片回收释放内存操作,方法大致和ViewPager+View方式相同,具体完整代码如下:
public class MyAdapter extends FragmentPagerAdapter{
private List<MyFragment> list;
public MyAdapter (FragmentManager fm, List<MyFragment> list) {
super(fm);
this.list=list;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
//回收图片,释放内存
ViewPager vpContainer = (ViewPager)container;
View view = vpContainer.getChildAt(position);
if(view!=null){
NotRecycledImageView imageView = (NotRecycledImageView)view.findViewById(R.id.iv_lock_screen_image);
releaseImageViewResouce(imageView);
}
}
@Override
public Fragment getItem(int position) {
return list.get(position);
}
@Override
public int getCount() {
return list.size();
}
/**
* 释放图片资源的方法
* @param imageView
*/
public void releaseImageViewResouce(ImageView imageView) {
if (imageView == null) return;
Drawable drawable = imageView.getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap=null;
}
}
System.gc();
}
}
3、这里不同于ViewPager+View方式的是,如果实际开发中在回收图片之前添加了将图片引用赋值为null后,出现图片不再加载的情况的话,就不能使用将图片引用赋值为null的方式了,但这样就会引起Canvas: trying to use a recycled bitmap异常,意思是正在使用一个已经回收过的bitmap,这样是不对的,所以需要在ImageView中的onDraw方法中捕获异常,因此,就需要自定义一个ImageView了,自定义ImageView代码很简单,如下代码只需try{}catch(){}即可。
public class NotRecycledImageView extends ImageView{
public NotRecycledImageView(Context context) {
super(context);
}
public NotRecycledImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
try {
super.onDraw(canvas);
} catch (Exception e) {
LogUtil.i("NotRecycledImageView.onDraw->exception=Canvas: trying to use a recycled bitmap");
}
}
}
三、产生的问题
1、为什么以下代码使用软引用SoftReference不能实现回收图片的功能?
SoftReference<Bitmap> wr = cache.get(position);
if(wr!=null&&wr.get()!=null&&!wr.get().isRecycled()){
Bitmap bitmap = wr.get();
bitmap.recycle();
bitmap=null;
}
2、需不需要使用软引用SoftReference来缓存图片?需要的话如何实现?