Android 自定义 ImageLoader

本文详细阐述了一种高效的网络图片加载策略,包括缓存管理、本地存储优化以及并发加载机制,旨在提升用户体验并减少服务器负载。通过实现自定义的图片加载器,实现在不同设备和网络环境下快速、高效地加载和显示图片资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;


import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.support.v4.util.LruCache;
import android.test.UiThreadTest;
import android.text.TextUtils;
import android.widget.ImageView;

/**
 * 网络图片加载
 * 
 * @author 
 *
 */
public class ImageNetLoader {

	private static final int TIMEOUT = 30 * 1000;
	// protected final HashMap<String, SoftReference<Bitmap>> bitmapMap;
	Context mContext;
	BitmapFactory.Options opts = null;
	@SuppressWarnings("rawtypes")
	LruCache mMemoryCache = null;
	private static ImageNetLoader imageNetLoader;
	
	private BlockingQueue<FutureTask<Bitmap>> blockingqeque = new LinkedBlockingQueue<FutureTask<Bitmap>>();
	private Thread loadThread = new Thread(){
		public void run() {
			while(true){
				FutureTask<Bitmap> task;
				try {
					task = blockingqeque.take();
					if(task.isCancelled() ||task.isDone() )
						continue;
					task.run();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				
					
			}
		};
	};
	
	public interface LocalFileNameBuilder {
		public String getName(String url, String localDir);
	}

	private ImageNetLoader(Context mContext) {
		this.mContext = mContext;
		int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		// 使用最大可用内存值的1/8作为缓存的大小。
		int cacheSize = maxMemory / 8;
		mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
			@Override
			protected int sizeOf(String key, Bitmap bitmap) {
				// 重写此方法来衡量每张图片的大小,默认返回图片数量。
				return bitmap.getByteCount() / 1024;
			}
		};
		
	}

	public static ImageNetLoader instance(Context context) {
		if (imageNetLoader == null) {
			synchronized (ImageNetLoader.class) {
				if (imageNetLoader == null) {
					imageNetLoader = new ImageNetLoader(context);
					imageNetLoader.loadThread.setPriority(Thread.MIN_PRIORITY);
					imageNetLoader.loadThread.start();
				}
			}
		}

		return imageNetLoader;
	}
	@SuppressWarnings("unchecked")
	public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
		if (getBitmapFromMemCache(key) == null) {
			mMemoryCache.put(key, bitmap);
		}
	}

	@SuppressWarnings("unchecked")
	public Bitmap getBitmapFromMemCache(String key) {
		return (Bitmap) mMemoryCache.get(key);
	}

	@SuppressWarnings("unchecked")
	public void removeFromCache(String key) {
		if (mMemoryCache.get(key) != null) {
			mMemoryCache.remove(key);
		}
	}

	/**
	 * 获取URL 并且保存到本地
	 * 
	 * @param urlString
	 * @param localDir
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public InputStream getInputStream(String urlString, String localDir,
			LocalFileNameBuilder builder) throws ClientProtocolException,
			IOException {

		HttpParams params = new BasicHttpParams();
		// add the timeout
		HttpConnectionParams.setConnectionTimeout(params, TIMEOUT);
		HttpConnectionParams.setSoTimeout(params, TIMEOUT);

		DefaultHttpClient httpClient = new DefaultHttpClient(params);
		HttpGet request = new HttpGet(urlString);
		HttpResponse response = httpClient.execute(request);

		if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
			request.abort();
			return null;
		}
		InputStream in = response.getEntity().getContent();

		String localPath = getLocalPath(urlString, localDir, builder);
		// 保存到本地

		File f = new File(localPath);
		if (f.createNewFile()) {
			FileOutputStream fo = new FileOutputStream(f);

			BufferedInputStream bi = new BufferedInputStream(in);
			byte[] buffer = new byte[1024 * 10];
			int len = 0;
			while ((len = bi.read(buffer)) > 0) {
				fo.write(buffer, 0, len);
				fo.flush();
			}

			bi.close();
			fo.close();
			return new FileInputStream(f);
		}
		return in;

	}

	/**
	 * 初始化 Bitmap参数
	 */
	private void initOptions() {
		if (opts == null) {
			opts = new BitmapFactory.Options();
			opts.inPreferredConfig = Bitmap.Config.RGB_565;
			opts.inSampleSize = 1;
		}
	}

	/**
	 * 
	 * @param urlString
	 *            网路路径
	 * @param localDir
	 *            本地存储路径 如果不为null就保存到相应目录 否则保存到 temp目录
	 * 
	 * @return
	 */
	public Bitmap getBitmapByUrl(String urlString, String localDir,
			LocalFileNameBuilder builder) {
		if (TextUtils.isEmpty(urlString)) {
			return null;
		}

		Log.v("ImageLoader", "ImageLoader load for:"+ urlString );
		try {
			return getBitmapFromUrl(urlString, localDir, builder);
		} catch (OutOfMemoryError e) {
			e.printStackTrace();
		} catch (Exception e) {
			Log.v(this.getClass().getSimpleName(),
					"Network Exception fetchDrawable failed 1st time" + e);
			try {
				return getBitmapFromUrl(urlString, localDir, builder);
			} catch (OutOfMemoryError es) {
				es.printStackTrace();
			} catch (Exception ex) {
				ex.printStackTrace();
				Log.v(this.getClass().getSimpleName(),
						"Network Exception fetchDrawable failed 2nd time" + ex);
			}
		}
		return null;
	}

	/**
	 * 保存网络文件到本地
	 * 
	 * @param urlString
	 * @param localDir
	 * @param builder
	 */
	public void loadUrlTolocal(String urlString, String localDir,
			LocalFileNameBuilder builder) {
		String localfile = getLocalPath(urlString, localDir, builder);
		File f = new File(localfile);
		if (f.exists() && f.canRead()) {
			return;
		}

		try {
			InputStream in = getInputStream(urlString, localDir, builder);
			in.close();
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 获取本地保存路径
	 * 
	 * @param urlString
	 * @param localDir
	 * @return
	 */
	private String getLocalPath(String urlString, String localDir,
			LocalFileNameBuilder builder) {
		String localPath;
		// 获取本地路径
		if (localDir != null) {
			

			if (builder == null) {
				String fileName = MD5.md5Lower(urlString.substring(
						urlString.lastIndexOf('/'), urlString.length())
						+ "."
						+ urlString.substring(urlString.lastIndexOf('.'),
								urlString.length()));
				localPath = localDir + "/" + fileName;
			} else {
				localPath = localDir + "/"
						+ builder.getName(urlString, localDir);
			}

		} else {
			localPath = Config.EXTERNAL_PATH + "/temp/" + MD5.md5Lower(urlString);
		}
		return localPath;

	}

	/**
	 * 从IN中获取bitmap
	 * 
	 * @param in
	 * @return
	 */
	private Bitmap getBitmapFromInputStream(InputStream in) {
		Bitmap bitmap = null;
		try {
			initOptions();
			bitmap = BitmapFactory.decodeStream(in, null, opts);
			try {
				in.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (OutOfMemoryError e) {
			return null;
		}
		return bitmap;
	}

	/**
	 * get Bitmap from the input stream
	 * 
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	private Bitmap getBitmapFromUrl(String urlString, String localDir,
			LocalFileNameBuilder builder) throws ClientProtocolException,
			IOException {
		return getBitmapFromInputStream(getInputStream(urlString, localDir,
				builder));
	}

	/**
	 * 获取网络图片 不永久保存图片
	 * 
	 * @param urlString
	 * @param defaultres
	 * @param progressDialog
	 * @param imageView1
	 */
	public void loadBitmapOnThread(final String urlString,
			final int defaultres, final ProgressDialog progressDialog,
			final ImageView imageView1) {
		loadBitmapOnThread(urlString, defaultres, progressDialog, imageView1,
				null);

	}

	/**
	 * 加载图片 文件名使用默认规则 默认加入到缓存中
	 * 
	 * @param urlString
	 * @param defaultres
	 * @param progressDialog
	 * @param imageView1
	 * @param localDir
	 */
	public void loadBitmapOnThread(final String urlString,
			final int defaultres, final ProgressDialog progressDialog,
			final ImageView imageView1, final String localDir) {

		loadBitmapOnThread(urlString, defaultres, progressDialog, imageView1,
				localDir, null);
	}

	/**
	 * 加载图片 默认加入到缓存中
	 * 
	 * @param urlString
	 * @param defaultres
	 * @param progressDialog
	 * @param imageView1
	 * @param localDir
	 * @param builder
	 *            本地文件名生成规则
	 */

	public void loadBitmapOnThread(final String urlString,
			final int defaultres, final ProgressDialog progressDialog,
			final ImageView imageView1, final String localDir,
			final LocalFileNameBuilder builder) {

		loadBitmapOnThread(urlString, defaultres, progressDialog, imageView1,
				localDir, builder, true);
	}

	/**
	 * 
	 * @author popo
	 */

	class IRunnable implements Callable<Bitmap> {

		String url;
		String localDir;
		LocalFileNameBuilder localFileNameBuilder;
		boolean saveToCache;
		ImageView imageView;
		ProgressDialog progressDialog;
		int defaultres;

		public IRunnable(String url, String localDir,
				LocalFileNameBuilder localFileNameBuilder, boolean saveToCache,
				ImageView imageView, ProgressDialog progressDialog,
				int defaultres) {
			this.url = url;
			this.localDir = localDir;
			this.localFileNameBuilder = localFileNameBuilder;
			this.saveToCache = saveToCache;
			this.imageView = imageView;
			this.progressDialog = progressDialog;
			this.defaultres = defaultres;
		}

		@Override
		public Bitmap call() throws Exception {
			Bitmap drawable = null;
			drawable = getBitmapByUrl(url, localDir, localFileNameBuilder);

			if (drawable != null) {
				Object[] obj = new Object[] { drawable, imageView, url,
						defaultres, saveToCache, progressDialog };
				Message message = handler.obtainMessage(1, obj);
				handler.sendMessage(message);
			}
			if (progressDialog != null) {
				progressDialog.dismiss();
			}
			taskMaps.remove(url);
			return drawable;

		}

	}

	class Node {
		public IRunnable runnable;
		public FutureTask<Bitmap> task;
	}

	/**
	 * ConcurrentHashMap or HashTable
	 */
	Hashtable<String, Node> taskMaps = new Hashtable<String, Node>();

	/**
	 * 加载图片
	 * 
	 * @param urlString
	 * @param defaultres
	 * @param progressDialog
	 * @param imageView1
	 * @param localDir
	 *            本地缓存目录
	 * @param builder
	 *            本地文件名生成规则
	 * @param saveToCache
	 *            是否 加入到缓存
	 */
	public void loadBitmapOnThread(final String urlString,
			final int defaultres, final ProgressDialog progressDialog,
			final ImageView imageView1, final String localDir,
			final LocalFileNameBuilder builder, final boolean saveToCache) {
		if(imageView1 == null)
			return ;
		Log.v("ImageLoader"," image load for:"+urlString);
		
		// 获取以前的Tag
		String tag = (String) imageView1.getTag(R.id.img_tag);

		imageView1.setTag(R.id.img_tag, urlString);
		if (urlString == null || urlString.trim().equals("")) {
			if (imageView1.getTag(R.id.img_tag).equals(urlString))
				imageView1.setImageResource(defaultres);

			if (progressDialog != null) {
				progressDialog.dismiss();
			}
			
			//取消图片之前的任务
			cancelTask(tag);

			return;
		}

		// 从缓存读取
		try {
			Bitmap bm = getBitmapFromMemCache(urlString);
			if (bm != null && !bm.isRecycled()
					&& imageView1.getTag(R.id.img_tag).equals(urlString)) {
				Log.v("from cache", "url:" + urlString + ",imageView:"
						+ imageView1.toString());
				imageView1.setImageBitmap(bm);
				if (progressDialog != null) {
					progressDialog.dismiss();
				}
				//取消图片之前的任务
				cancelTask(tag);
				return;
			} else {
				removeFromCache(urlString);
				if (defaultres > 0)
					imageView1.setImageResource(defaultres);
				
			}
		} catch (OutOfMemoryError e) {
			e.printStackTrace();
		}
		final String localPath = getLocalPath(urlString, localDir, builder);
		if (localPath != null) {
			imageView1.setTag(R.id.localimg_tag, localPath);
		}
		File f = new File(localPath);
		if (f.exists() && f.canRead()) {
			try {
				FileInputStream fi = new FileInputStream(f);
				Bitmap b = getBitmapFromInputStream(fi);
				if (b != null
						&& imageView1.getTag(R.id.localimg_tag).equals(
								localPath)) {
					Log.v("from file", "url:" + urlString + ",localPath"
							+ localPath);
					if (saveToCache) {
						if (!b.isRecycled())
							addBitmapToMemoryCache(urlString, b);
					}
					imageView1.setImageBitmap(b);
					//取消图片之前的任务
					cancelTask(tag);
					return;
				}
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (OutOfMemoryError e) {
				e.printStackTrace();
			}

		}

		if (progressDialog != null)
			progressDialog.show();

		//那连个地址一样怎么办捏
		//同一个变相同地址 就不做任何处理了等着吧
		
		if(tag != null && tag.equals(urlString)){
			
		}else{
			//不一样取消之前的任务
			cancelTask(tag);
			final Node node = taskMaps.get(urlString);
			//之前已经有图片在下载
			if(node != null ){
				
				
				FutureTask<Bitmap> task = new FutureTask<Bitmap>(new Callable<Bitmap>() {

					@Override
					public Bitmap call() throws Exception {
						try {
							Bitmap drawable = node.task.get();
							if (drawable != null) {
								Object[] obj = new Object[] { drawable, imageView1, urlString,
										defaultres, saveToCache, progressDialog };
								Message message = handler.obtainMessage(1, obj);
								handler.sendMessage(message);
							}

						} catch (InterruptedException e) {
							e.printStackTrace();
						} catch (ExecutionException e) {
							e.printStackTrace();
						}
						return null;
					}
				});
				
				blockingqeque.offer(task);
				
			}else{
				IRunnable runnable = new IRunnable(urlString, localDir, builder, saveToCache, imageView1, progressDialog, defaultres);
				FutureTask<Bitmap> task =  new FutureTask<Bitmap>(runnable);
				blockingqeque.offer(task);
				Node n = new Node();
				n.runnable = runnable;
				n.task = task;
				taskMaps.put(urlString, n);
			}
		}
	}
	
	/**
	 * 取消图片任务
	 * @param tag
	 */
	private void cancelTask(String tag) {
		// 现在图片已经不需要新下载的图片了 那么取消掉吧
		if(tag == null)
			return;
		Node node = taskMaps.get(tag);
		if (node != null) {
			boolean canceled = node.task.cancel(false);
			if (canceled && node.task.isCancelled()) {
				taskMaps.remove(tag);
			}

		}
	}

	final Handler handler = new Handler(Looper.getMainLooper()) {
		@Override
		public void handleMessage(Message message) {

			ImageView imageView = (ImageView) ((Object[]) message.obj)[1];
			String imgpath = (String) ((Object[]) message.obj)[2];
			int defaultres = (Integer) ((Object[]) message.obj)[3];
			boolean saveToCache = (Boolean) ((Object[]) message.obj)[4];
			if (((Object[]) message.obj)[5] != null) {
				ProgressDialog pd = (ProgressDialog) ((Object[]) message.obj)[5];
				if (pd != null) {
					pd.dismiss();
				}
			}

			Log.v("NETIMAGE", "url:" + imgpath + ",imageView:" + imageView);
			switch (message.what) {
			case 1:
				try {
					Bitmap bm = null;
					if (null == message.obj) {
						if (defaultres > 0)
							imageView.setImageResource(defaultres);
					} else {
						bm = (Bitmap) ((Object[]) message.obj)[0];

						if (!bm.isRecycled()) {
							if (imageView != null
									&& imageView.getTag(R.id.img_tag) != null
									&& imageView.getTag(R.id.img_tag).equals(
											imgpath))

								imageView.setImageBitmap(bm);
							removeFromCache(imgpath);
							addBitmapToMemoryCache(imgpath, bm);
						} else {
							if (imageView != null && defaultres > 0)
								imageView.setImageResource(defaultres);
						}
					}
				} catch (OutOfMemoryError e) {
					e.printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				}

				break;
			}
		}

	};

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值