图片的异步加载(双缓存)+ViewHolder

图片的异步加载(双缓存)+ViewHolder

         上次只是提到了本地图片的异步加载,最后还出现了OOM,不过最后解决了(BaseApapter解决OOM问题)。这次就给大家带来这个网络图片的异步加载+双缓存+ViewHolder。我先叙述一下异步加载的原理,说的通俗一点就是UI主线程继续做与用户交互的响应监听和操作,而加载图片的任务交到其他线程中去做,当图片加载完成之后,再跟据某种机制(比如回调)绘制到要显示的控件中。

      首先,贴出AsyncImageLoader.java,这个类是关键,主要做的就是当加载图片的时候,去缓冲区查找,如果有的话,则立刻返回Bitmap对象,省掉再去网络服务器下载的时间和流量

package com.tz.asyncimageload;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

/**
 * Created 2015/9/9 Adminstrator tianzhao
 */
public class AsyncImageLoader {

	/**
	 * 内存图片软引用缓存
	 */
	private HashMap<String, SoftReference<Bitmap>> imageCache = null;

	private final static String CACHE_PATH = Environment
			.getExternalStorageDirectory().getPath() + "/AsyncLoad";

	public AsyncImageLoader() {
		System.out.println("path-->" + CACHE_PATH);
		imageCache = new HashMap<String, SoftReference<Bitmap>>();
	}

	@SuppressLint("HandlerLeak")
	public Bitmap loadBitmap(final ImageView imageView, final String imageURL,
			final ImageCcllBack imageCcllBack) {

		/**
		 * 在内存缓存中,则返回Bitmap对象
		 */
		if (imageCache.containsKey(imageURL)) {
			SoftReference<Bitmap> reference = imageCache.get(imageURL);
			Bitmap bitmap = reference.get();
			if (null != bitmap) {
				return bitmap;
			}
		} else {
			/**
			 * 在本地缓存中查找
			 */
			String bitmapName = imageURL
					.substring(imageURL.lastIndexOf("/") + 1);
			File cacheDir = new File(CACHE_PATH);
			File[] cacheFiles = cacheDir.listFiles();
			if (cacheFiles != null) {
				int i = 0;
				for (; i < cacheFiles.length; i++) {
					if (bitmapName.equals(cacheFiles[i].getName())) {
						break;
					}
				}
				// 找到,则返回该图片的Bitmap
				if (i < cacheFiles.length) {
					Bitmap bitmap = BitmapFactory.decodeFile(CACHE_PATH + "/"
							+ bitmapName);
					imageCcllBack.imageLoadSuccess(imageView, bitmap);
					return bitmap;
				}
			}
		}
		/**
		 * 两个缓存中都没有的话则开启新线程进行下载
		 */
		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				if (0 == msg.what) {
					imageCcllBack.imageLoadSuccess(imageView, (Bitmap) msg.obj);
				} else if (1 == msg.what) {
					imageCcllBack.imageLoadFaluire(imageView);
				}
			}
		};

		new Thread() {
			@Override
			public void run() {
				Bitmap bitmap = null;
				try {
					URL url = new URL(imageURL);
					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.setDoInput(true);
					conn.connect();
					InputStream bitmapIs = conn.getInputStream();
					bitmap = BitmapFactory.decodeStream(bitmapIs);
					bitmapIs.close();

					imageCache.put(imageURL, new SoftReference<Bitmap>(bitmap));
					Message msg = Message.obtain();
					msg.obj = bitmap;
					msg.what = 0;
					handler.sendMessage(msg);

				} catch (MalformedURLException e) {
					e.printStackTrace();
					handler.sendEmptyMessage(1);
				} catch (IOException e) {
					e.printStackTrace();
					handler.sendEmptyMessage(1);
				}

				if (null != bitmap) {
					/**
					 * 将图片保存到本地缓存
					 */
					File dir = new File(CACHE_PATH);
					if (!dir.exists()) {
						dir.mkdirs();
					}
					File bitmapFile = new File(CACHE_PATH + "/"
							+ imageURL.substring(imageURL.lastIndexOf("/") + 1));
					if (!bitmapFile.exists()) {
						try {
							bitmapFile.createNewFile();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					FileOutputStream fos;
					try {
						fos = new FileOutputStream(bitmapFile);
						bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
						fos.close();
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else {
					handler.sendEmptyMessage(1);
				}
			}
		}.start();
		return null;
	}

	public interface ImageCcllBack {
		public void imageLoadSuccess(ImageView imageView, Bitmap bitmap);

		public void imageLoadFaluire(ImageView imageView);
	}
}
         PS:我这里用到了两个缓冲,一是内存缓存,一个是本地缓存(即SD卡缓存),其中用到了SoftReference,这个类的主要作用是生成一个“软引用”,你可以认为是一种随时会由于JVM垃圾回收机制回收掉的Map对象(而平时我们所用到的引用不释放的话不会被JVM回收),之所以用到软引用,就是考虑到android对图片的缓存是有大小限制的,当超过这个大小时,就一定要释放,如果你用引用,保持不释放的话,那么FC(Force close)就离你不远了。。。我这里还用到了一个本地缓存的机制,是和参考博客不太一样的地方,只是提供一种思路,方法还没有完善(主要因为和服务器还没约定好关于图片的命名规则),主要作用是在用户浏览过大量图片之后(超过内存缓存容量之后),保留在本地,一是为了提高读取速度,二是可以减少流量消耗!


接下来就是Adapter类了,这里继承BaseAdapter类MyAdapter.java

package com.tz.asyncimageload;

import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import com.example.asyncimageload.R;

/**
 * Created 2015/9/9 Adminstrator tianzhao
 */
public class MyAdapter extends BaseAdapter {

	private Context context;
	private List<String> imgList;
	private AsyncImageLoader asyncImageLoader;

	public MyAdapter(Context context, List<String> imgList) {
		this.context = context;
		this.imgList = imgList;
		asyncImageLoader = new AsyncImageLoader();
	}

	@Override
	public int getCount() {
		return null == imgList ? 0 : imgList.size();
	}

	@Override
	public Object getItem(int position) {
		return imgList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = null;
		if (null == convertView) {
			viewHolder = new ViewHolder();
			LayoutInflater mInflater = LayoutInflater.from(context);
			convertView = mInflater.inflate(R.layout.item_layout, null);

			viewHolder.textView = (TextView) convertView.findViewById(R.id.tv);
			viewHolder.imageView = (ImageView) convertView
					.findViewById(R.id.iv);

			convertView.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) convertView.getTag();
		}

		viewHolder.textView.setText(getItem(position).toString());
		viewHolder.imageView.setBackgroundResource(R.drawable.no_data);
		asyncImageLoader.loadBitmap(viewHolder.imageView, getItem(position)
				.toString(), new AsyncImageLoader.ImageCcllBack() {
			@Override
			public void imageLoadSuccess(ImageView imageView, Bitmap bitmap) {
				imageView.setImageBitmap(bitmap);
			}

			@Override
			public void imageLoadFaluire(ImageView imageView) {
				imageView.setImageResource(R.drawable.no_data);
			}
		});

		return convertView;
	}

	private static class ViewHolder {
		ImageView imageView;
		TextView textView;
	}
}

          asyncImageLoader是我们定义的异步加载类的对象,通过这个类的public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)加载图片,传递参数也与参考博客有些不同,我觉得这样更好理解一下,就是要显示图片的URL链接,和图片要显示对应的控件,当然最重要的还有这个接口实现的回调对象,是在线程中下载完图片之后,用以加载图片的回调对象。而这个回调对象在传递的时候已经实现了接口方法,即将下载好的图片绘制在对应的控件之中


接下来就是MainActivity.java了

package com.tz.asyncimageload;

import java.util.ArrayList;
import java.util.List;
import com.example.asyncimageload.R;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

public class MainActivity extends Activity {
	private ListView mListView;
	private List<String> imgList = new ArrayList<String>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		imgList.add("http://img0.imgtn.bdimg.com/it/u=3292852797,3921168757&fm=21&gp=0.jpg");
		imgList.add("http://img4.imgtn.bdimg.com/it/u=1268041620,2779298692&fm=21&gp=0.jpg");
		imgList.add("http://img0.imgtn.bdimg.com/it/u=2818850098,881187710&fm=21&gp=0.jpg");
		imgList.add("http://img3.yxlady.com/mr/UploadFiles_9207/20150715/20150715163557826.jpg");
		imgList.add("http://img0.imgtn.bdimg.com/it/u=2949470383,236653565&fm=21&gp=0.jpg");
		imgList.add("http://d03.res.meilishuo.net/pic/r/36/48/488a12972a38065cd9d98495ca12_800_800.jpeg");
		imgList.add("http://pic.wenwen.soso.com/p/20101227/20101227153650-1873348623.jpg");
		imgList.add("http://img3.imgtn.bdimg.com/it/u=1054064343,3338205640&fm=21&gp=0.jpg");
		imgList.add("http://img3.imgtn.bdimg.com/it/u=1025044414,887723369&fm=21&gp=0.jpg");
		imgList.add("http://file.azg168.cn/file/mianxiangdaquan/xiangshu/20141006/bffcb52bd45827e6f05621dd259c7ff2.jpg");
		imgList.add("http://img0.imgtn.bdimg.com/it/u=3427470176,4008266000&fm=21&gp=0.jpg");

		mListView = (ListView) findViewById(R.id.lv);

		MyAdapter myAdapter = new MyAdapter(MainActivity.this, imgList);
		mListView.setAdapter(myAdapter);
	}

}

     OK!网络图片的异步加载+双缓存+ViewHolder到这里接结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 ListView 异步图片时,可以通过以下步骤实现: 1. 创建一个自定义的适配器(Adapter)类,继承自 BaseAdapter。这个适配器将负责管理数据和视图的绑定。 2. 在适配器中,创建一个内部类 ViewHolder,用于保存列表项中的视图引用。这个类将包含一个 ImageView 用于显示图片。 3. 在适配器的 getView 方法中,获取当前列表项的数据,并更新 ViewHolder 中的 ImageView。 4. 在更新 ImageView 时,可以使用异步图片的第三方库,如 Glide 或 Picasso。这些库提供了简单的接口来网络图片,并且处理了图片缓存和压缩等问题。 下面是一个简单的示例代码: ```java public class CustomAdapter extends BaseAdapter { private List<String> imageUrlList; private Context context; public CustomAdapter(Context context, List<String> imageUrlList) { this.context = context; this.imageUrlList = imageUrlList; } @Override public int getCount() { return imageUrlList.size(); } @Override public Object getItem(int position) { return imageUrlList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false); viewHolder = new ViewHolder(); viewHolder.imageView = convertView.findViewById(R.id.image_view); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } String imageUrl = imageUrlList.get(position); // 使用 Glide 异步图片 Glide.with(context) .load(imageUrl) .placeholder(R.drawable.placeholder_image) // 中显示的占位图 .error(R.drawable.error_image) // 失败显示的错误图 .into(viewHolder.imageView); return convertView; } static class ViewHolder { ImageView imageView; } } ``` 在上述示例代码中,CustomAdapter 是自定义的适配器类,其中的 getView 方法中使用了 Glide 来异步图片。你可以将 imageUrlList 替换为你自己的图片地址列表,并根据需要修改中和失败时显示的图片资源。同时,记得在布局文件中定义好 ImageView 的 id。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值