由于获取图片从来是我的弱项,因此特地记录下方法
先建立一个被其它类调用的方法类:ShowImage()
package chj.weibo.imgutil; import chj.weibo.app.WeiboApplication; import android.graphics.Bitmap; import android.widget.ImageView; public class ShowImageLoader { public void showImg(ImageView view,String url){ view.setTag(url); view.setImageBitmap(WeiboApplication.loader.get(url, getCallback(view,url))); } private static ImageLoaderCallback getCallback(final ImageView view,String url){ return new ImageLoaderCallback() { @Override public void refresh(String url, Bitmap bmp) { if(url.equals(view.getTag().toString())){ view.setImageBitmap(bmp); } } }; } }
然后建立一个ImageLoader:
package chj.weibo.imgutil; import java.lang.Thread.State; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import chj.weibo.app.WeiboApplication; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.Message; public class LazyImgLoader { public static final int SUCCESS_DOWM = 1; public static final String IMG_URL="img_url"; public static final String IMG = "img"; private ImageManager imgManager = new ImageManager(WeiboApplication.context); private BlockingQueue<String> urlQueue = new ArrayBlockingQueue<String>(20); private DownloadImgThread thread = new DownloadImgThread(); private CallbackManager manager = new CallbackManager(); public Bitmap get(String url,ImageLoaderCallback callback){ try { Bitmap bmp = null; //首先从缓存获取 if(imgManager.contains(url)){ bmp = imgManager.getFromCache(url); return bmp; } else{ manager.put(url, callback); //开始从网络下载 startDownloadThread(url); } } catch (Exception e) { e.printStackTrace(); } return null; } private void startDownloadThread(String url) throws Exception{ //从网络下载 if(!urlQueue.contains(url)){ //放入下载队伍 urlQueue.put(url); } State s = thread.getState(); if(s == State.NEW){ thread.start(); } //线程已经结束 else if(s == State.TERMINATED){ thread = new DownloadImgThread(); thread.start(); } } Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch(msg.what){ case SUCCESS_DOWM: Bundle b = msg.getData(); String url = b.getString(IMG_URL); Bitmap bmp = b.getParcelable(IMG); manager.callback(url,bmp); } } }; private class DownloadImgThread extends Thread{ private boolean isRun = true; @Override public void run() { try { while(isRun){ String u = urlQueue.poll(); Bitmap bmp; if(null == u){ //下载完毕 break; } if(imgManager.getFromFile(u) == null){ bmp = imgManager.downloadFromNet(u); } else{ bmp = imgManager.getFromFile(u); } Message msg = handler.obtainMessage(SUCCESS_DOWM); Bundle b = msg.getData(); b.putString(IMG_URL, u); b.putParcelable(IMG, bmp); handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); }finally{ isRun = false; } } } }
ImageLoader会调用一下从缓存读取图片或者从互联网下载图片的方法,因此需要一个ImageManager的类:
package chj.weibo.imgutil; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; import chj.weibo.util.MD5Util; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class ImageManager { private Context context; private Map<String, SoftReference<Bitmap>> imgCache ; public ImageManager(Context context) { this.context = context; imgCache = new HashMap<String, SoftReference<Bitmap>>(20); } public boolean contains(String url){ return imgCache.containsKey(url); } /** * 从缓存获取图片 * @param url * @return */ public Bitmap getFromCache(String url){ Bitmap bmp = null; //先从缓存读取 bmp = this.getFromMap(url); if(null == bmp){ //从文件读取 bmp = this.getFromFile(url); imgCache.put(url, new SoftReference<Bitmap>(bmp)); } return bmp; } /** * 从文件读取图片 * @param url * @return */ public Bitmap getFromFile(String url){ Bitmap bmp = null; InputStream is = null; String fileName = MD5Util.getMD5String(url); try { is = context.openFileInput(fileName); if(null != is){ bmp = BitmapFactory.decodeStream(is); return bmp; } } catch (Exception e) { e.printStackTrace(); return null; }finally{ try { if(is != null){ is.close(); is = null; } } catch (IOException e) { e.printStackTrace(); } } return null; } /** * 从Map读取图片 * @param url * @return */ public Bitmap getFromMap(String url){ Bitmap bmp = null; SoftReference<Bitmap> ref = null; synchronized (this) { ref = imgCache.get(url); } if(null != ref){ bmp = ref.get(); } return bmp; } /** * 从网络下载图片并把他放入文件系统中 * @param url * @return */ public Bitmap downloadFromNet(String url){ if(url == null || "".equals(url)){ return null; } try { URL u = new URL(url); HttpURLConnection conn = (HttpURLConnection)u.openConnection(); String fileName = writeToFile(url, conn.getInputStream()); return BitmapFactory.decodeFile(fileName); } catch (Exception e) { e.printStackTrace(); } return null; } //将图片写入文件系统 private String writeToFile(String fileName,InputStream is){ BufferedOutputStream bos = null; BufferedInputStream bis = null; byte[] buff = new byte[1024]; int length = 0; String fileNameMD5 = null; try { fileNameMD5 = MD5Util.getMD5String(fileName); bis = new BufferedInputStream(is); bos = new BufferedOutputStream(context.openFileOutput(fileNameMD5, Context.MODE_PRIVATE)); while((length = bis.read(buff)) != -1){ bos.write(buff, 0, length); } } catch (Exception e) { return null; } finally{ try { if(null != is){ is.close(); is = null; } if(null != bis){ bis.close(); bis = null; } if(null != bos){ bos.flush(); bos.close(); bos = null; } } catch (Exception e2) {} } return context.getFilesDir() + "/" + fileNameMD5; } }
然后需要一个接口做图片回调用:ImageLoaderCallback:
package chj.weibo.imgutil; import android.graphics.Bitmap; public interface ImageLoaderCallback { public void refresh(String url,Bitmap bmp); }
最后需要一个CallbackManager:
package chj.weibo.imgutil; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import android.graphics.Bitmap; public class CallbackManager { private ConcurrentHashMap<String, List<ImageLoaderCallback>> maps; public CallbackManager() { maps = new ConcurrentHashMap<String, List<ImageLoaderCallback>>(); } public void put(String url,ImageLoaderCallback cb){ if(!maps.contains(url)){ maps.put(url, new ArrayList<ImageLoaderCallback>()); } maps.get(url).add(cb); } public void callback(String url,Bitmap bmp){ List<ImageLoaderCallback> callback = maps.get(url); if(null == callback){ return; } for (ImageLoaderCallback c : callback) { c.refresh(url, bmp); } } }
然后直接在需要使用的类中调用showImage即可.
由于Android的openfileoutput和input传入的字符串不可带有分隔符,因此另外需要一个类用于加密,这里采用MD5加密算法:
package chj.weibo.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Util { /** * 16进制字符集 */ private static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /** * 指定算法为MD5的MessageDigest */ private static MessageDigest messageDigest = null; /** * 初始化messageDigest的加密算法为MD5 */ static { try { messageDigest = MessageDigest.getInstance("MD5"); } catch(NoSuchAlgorithmException e) { e.printStackTrace(); } } /** * MD5加密字符串 * @param str 目标字符串 * @return MD5加密后的字符串 */ public static String getMD5String(String str) { return getMD5String(str.getBytes()); } /** * MD5加密以byte数组表示的字符串 * @param bytes 目标byte数组 * @return MD5加密后的字符串 */ public static String getMD5String(byte[] bytes) { messageDigest.update(bytes); return bytesToHex(messageDigest.digest()); } /** * 将字节数组转换成16进制字符串 * @param bytes 目标字节数组 * @return 转换结果 */ public static String bytesToHex(byte bytes[]) { return bytesToHex(bytes, 0, bytes.length); } /** * 将字节数组中指定区间的子数组转换成16进制字符串 * @param bytes 目标字节数组 * @param start 起始位置(包括该位置) * @param end 结束位置(不包括该位置) * @return 转换结果 */ public static String bytesToHex(byte bytes[], int start, int end) { StringBuilder sb = new StringBuilder(); for(int i = start; i < start + end; i++) { sb.append(byteToHex(bytes[i])); } return sb.toString(); } /** * 将单个字节码转换成16进制字符串 * @param bt 目标字节 * @return 转换结果 */ public static String byteToHex(byte bt) { return HEX_DIGITS[(bt & 0xf0) >> 4] + "" + HEX_DIGITS[bt & 0xf]; } }