仿新浪微博2014之登陆界面二(异步加载图片和缓存)

对于社交和资讯类应用,从网络获取图片并显示的功能必不可少了,异步加载图片和缓存,可以加快图片的显示和操作的流畅度,提高用户体验。

我先写了一个图片加载管理器ImageLoaderManager.java

代码如下:

package com.xhq.common;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Log;


public class ImageLoaderManager
{
	private final String TAG = "ImageLoaderManager";
	private LruCache<String, Bitmap> mCache = null;
	private String cacheDir = null;
	public ImageLoaderManager(LruCache<String, Bitmap> cache)
	{
		this.mCache = cache;
	}
	
	public void setCacheDir(String dir)
	{
		Log.d(TAG, "缓存目录:" + dir);
		this.cacheDir = dir;
	}

	public Bitmap getBitmapFromUrl(String url)
	{
		Log.d(TAG, "getBitmapFromUrl:" + url);
		Bitmap bitmap = null;
		try
		{
			URL u = new URL(url);
			HttpURLConnection conn = (HttpURLConnection) u.openConnection();
			InputStream is = conn.getInputStream();
			bitmap = BitmapFactory.decodeStream(is);
			Log.d(TAG, "save to cache:" + url);
			mCache.put(url, bitmap);
			String fileName = getMD5Str(url);
			String filePath = this.cacheDir  + fileName;
			try
			{
				FileOutputStream fos = new FileOutputStream(filePath);
				bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
				Log.d(TAG, "save to file:" + filePath);
			}catch(Exception e)
			{
				Log.d(TAG, " fail to save file:" + filePath);
			}
			is.close();
			conn.disconnect();
			return bitmap;
		} catch (IOException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	public Bitmap getBitmapFromCache(String url)
	{
		Bitmap bitmap = null;
		synchronized (mCache)
		{
			if (mCache.get(url) != null)
			{
				bitmap = mCache.get(url);
				if (bitmap != null)
				{
					return bitmap;
				}
			}
			Log.d(TAG, "get from cache:" + url);
			bitmap = getBitmapFromFile(url);
			if (bitmap != null)
			{
				mCache.put(url, bitmap);
			}
			return bitmap;
		}
	}

	private Bitmap getBitmapFromFile(String url)
	{
		Bitmap bitmap = null;
		String fileName = getMD5Str(url);
		if (fileName == null)
			return null;
		String filePath = cacheDir + "/" + fileName;
		try
		{
			FileInputStream fis = new FileInputStream(filePath);
			bitmap = BitmapFactory.decodeStream(fis);
		} catch (FileNotFoundException e)
		{
			e.printStackTrace();
			bitmap = null;
		}
		return bitmap;
	}

	/**
	 * MD5 加密
	 */
	private static String getMD5Str(String str)
	{
		MessageDigest messageDigest = null;
		try
		{
			messageDigest = MessageDigest.getInstance("MD5");
			messageDigest.reset();
			messageDigest.update(str.getBytes("UTF-8"));
		} catch (NoSuchAlgorithmException e)
		{
			System.out.println("NoSuchAlgorithmException caught!");
			return null;
		} catch (UnsupportedEncodingException e)
		{
			e.printStackTrace();
			return null;
		}

		byte[] byteArray = messageDigest.digest();
		StringBuffer md5StrBuff = new StringBuffer();
		for (int i = 0; i < byteArray.length; i++)
		{
			if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
				md5StrBuff.append("0").append(
						Integer.toHexString(0xFF & byteArray[i]));
			else
				md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
		}

		return md5StrBuff.toString();
	}
}

这个图片加载管理器功能主要是通过图片URL加载图片,分为从内存中加载图片和从网络加载图片,内存加载又分为从cache加载和从文件加载,先从cache加载,如果没有该图片就从文件加载。

从网络加载图片后会把图片保存到文件中,下次再加载相同的url图片时就不用从网络加载了。这里主要说一下LruCache  和MD5加密。

LruCache 使用了硬引用和Lru算法,Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。当cache已满的时候加入新的item时,在队列尾部的item会被回收。

 

这里对图片的URL进行MD5加密主要是为了更方便的在文件中保存和获取图片,因为url的和文件的路径比较相似,系统会识别不了正确的图片路径。

有了图片加载管理器,我们就进行图片的异步加载AsyncImageLoader.java

 

package com.xhq.common;

import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.xhq.xweibo.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Environment;
import android.os.Handler;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

public class AsyncImageLoader
{
	private final String TAG = "AsyncImageLoader";
	private static HashSet<String> mLoadingUrlSet;
	private LruCache<String, Bitmap> mMemoryCache;
	private static ImageLoaderManager mImldmg;
	private static ExecutorService mExecutorService;
	private Handler mHandler;
	private Activity mActitity;

	public interface onLoadCompleteListener
	{
		public void onLoadSuccess(View view,Bitmap bmp, String url);
		public void onLaodFail(View view,String url);
	}

	public AsyncImageLoader(Context context)
	{
		mLoadingUrlSet = new HashSet<String>();
		mHandler = new Handler();
		
		int maxMemory = (int) Runtime.getRuntime().maxMemory();
		int cacheSize = maxMemory / 8;// 给LruCache分配1/8 4M		
		mMemoryCache = new LruCache<String, Bitmap>(cacheSize)
		{
			// 必须重写此方法,来测量Bitmap的大小
			@Override
			protected int sizeOf(String key, Bitmap value)
			{
				return value.getRowBytes() * value.getHeight();
			}

		};
		mImldmg = new ImageLoaderManager(mMemoryCache);
		Log.d(TAG, "cachesize:" + cacheSize/1024/1024+"MB");
		
		startThreadPool(2);
		String appname = context.getResources().getString(R.string.app_name);
		String sdDir = "/sdcard/"+appname+"/cache/";
		String dataDir = context.getCacheDir().getAbsolutePath()+"/";
		
		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
		{
			if(FileUtil.isDirExist(sdDir, true))
			{
				setCacheDir(sdDir);
			}
			
		}else
		{
			setCacheDir(dataDir);
		}	
	}

	public void setCacheDir(String dir)
	{
		mImldmg.setCacheDir(dir);
	}

	/** 开启线程池 */
	public static void startThreadPool(int poolNum)
	{
		if (mExecutorService == null || mExecutorService.isShutdown()
				|| mExecutorService.isTerminated())
		{
			mExecutorService = Executors.newFixedThreadPool((poolNum == 0) ? 1
					: poolNum);
		}
	}
	public void loadImage(final ImageView image, final String url,
			final onLoadCompleteListener onLoadComplete)
	{
		if (mLoadingUrlSet.contains(url))
		{
			Log.d(TAG, url + " is loading");
			return;
		}
		Bitmap bitmap = null;
		bitmap = mImldmg.getBitmapFromCache(url);
		if (bitmap != null)
		{
			if (onLoadComplete != null)
			{
				onLoadComplete.onLoadSuccess(image, bitmap, url);
			}
		} else
		{
			// 从网络端下载图片
			mLoadingUrlSet.add(url);
			mExecutorService.submit(new Runnable()
			{
				@Override
				public void run()
				{
					final Bitmap bitmap = mImldmg.getBitmapFromUrl(url);
					mHandler.post(new Runnable()
					{
						@Override
						public void run()
						{
							if (onLoadComplete != null)
							{
								if(bitmap != null)
								{
									onLoadComplete.onLoadSuccess(image,bitmap, url);
								}
								else
								{
									onLoadComplete.onLaodFail(image,url);
								}
							}	
							mLoadingUrlSet.remove(url);						
						}
					});
				}
			});
		}
	}
}

 

这个图片异步加载类主要使用了线程、线程池和Handler,主要步骤:

1、设置LruCache的大小cachesize,当缓存的图片大于cachesize时,就会把队列未不的图片释放掉

2、设置图片文件的缓存路径,如果有SD卡,我们就设置缓存路径为  sdDir = "/sdcard/"+appname+"/cache/";

     否则就设置缓存路径为    dataDir = context.getCacheDir().getAbsolutePath()+"/";
     我们可以通过  Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 来判定是否有SD卡

 

3、开启线程池,由于微博从网络加载图片是比较频繁的,每加载一张图片都和服务器建立一个连接,那么服务器的压力就很大了,所以这里运用线程池来限制连接数

     线程池我们用了ExecutorService 来管理连接线程

4、最后就是在线程中加载图片了,这里运用了Thread 和Handler 来完成异步通知,当加载图片成功或者失败会通过Handler来通知UI线程更新界面

 

 

 

 

 

 

 

 

 

 


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值