public class ImageLoader { //下载任务执行完毕后发送的message的what public static final int LOAD_FINISH = 101; public static final int NEW_TASK = 102;
//线程池中线程的数量 //(这个数量应该与设备cpu的核数相同) public static int Thread_Count; public static Context c;//上下文 //内存缓存 public static LruCache<String, Bitmap> MemCache; //(双向)任务队列 //未来可以根据需求选择是FIFO还是LIFO public static LinkedBlockingDeque<Runnable> taskQueue; //线程池 public static ExecutorService exec; //从任务队列中取下载任务的handler public static Handler pollHandler; //任务下载完毕后,用来刷新ListView条目显示下载图片的Handler public static Handler uiHandler; //"养活"pollHandler public static Thread pollThread; //磁盘缓存 public static DiskLruCache DiskCache;
//标示的作用,用来确保ImageLoader只要进行一次 //初始化操作即可 public static boolean isFirstTime = true; /** * 初始化ImageLoader所有属性的值 * @param context */ public static void init(Context context){
if(!isFirstTime){ return; }
//c属性赋值 c = context; //获取cpu的核数为Thread_Count赋值 Thread_Count = getNumberOfCores(); //初始化内存缓存 MemCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory()/8)){ @Override protected int sizeOf(String key, Bitmap value) { return value.getHeight()*value.getRowBytes(); }
};
//初始化磁盘缓存 try { DiskCache = DiskLruCache.open(directory(), appVersion(), 1, 1024*1024*10); } catch (IOException e1) { e1.printStackTrace(); }
//初始化任务队列 taskQueue = new LinkedBlockingDeque<Runnable>(); exec = Executors.newFixedThreadPool(Thread_Count); //pollHandler, uiHandler uiHandler = new Handler(Looper.getMainLooper()){ @Override public void handleMessage(Message msg) { //利用线程池中执行完毕的内容,来刷新ListView if(msg.what==LOAD_FINISH){ //msg.obj到底放什么? //msg.obj放什么能解决ListView加载“动画”效果 ValueObject vo = (ValueObject) msg.obj; ImageView iv = vo.iv; String url = vo.url; Bitmap bitmap = vo.bitmap; if(iv.getTag().toString().equals(url)){ iv.setImageBitmap(bitmap); } }else{ super.handleMessage(msg); } } };
pollThread = new Thread(){ @Override public void run() { Looper.prepare();
pollHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what==NEW_TASK){ //现在任务队列中被放入了新“下载任务” //去队列中取任务 try { Runnable task = taskQueue.takeFirst();先进先出 //将取出的任务放到线程池中执行 exec.execute(task); } catch (InterruptedException e) { e.printStackTrace(); } }else{ super.handleMessage(msg); } }
}; Looper.loop(); }
};
pollThread.start();
isFirstTime = false;
} private static int appVersion() {
try { PackageInfo info = c.getPackageManager(). getPackageInfo(c.getPackageName(), 0); 获取app版本 return info.versionCode; } catch (NameNotFoundException e) { e.printStackTrace(); } return 1; } private static File directory() { //有些环境在执行getExternalCacheDir获取 //外部的缓存路径的时候可能会报空指针异常 //所以这里采用了内部缓存路径 String path = c.getCacheDir().getPath(); 外存getExternalCacheDir() return new File(path,"imageloadercache"); } /** * 获取url指向的图像,放到iv中显示 * @param url 图像路径 * @param iv 显示图像的ImageView */ public static void loadImage(final String url,final ImageView iv){ if(isFirstTime){ throw new RuntimeException("ImageLoader未做初始化"); }
//先判断,url所指向的图像是否在缓存中有保存
//url转为MD5格式的字符串
final String md5Url = getMD5(url); //设置TAG,到时候要与vo中的URL进行比对! iv.setTag(md5Url); Bitmap bitmap = MemCache.get(md5Url); if(bitmap!=null){ Log.d("TAG","图像是从内存缓存中加载的"); iv.setImageBitmap(bitmap); return; }
try { //从磁盘缓存中试图取出url所对应的图片 Snapshot snap = DiskCache.get(md5Url); if(snap!=null){ Log.d("TAG","图像是从磁盘缓存中获取的"); InputStream in = snap.getInputStream(0); bitmap = BitmapFactory.decodeStream(in); //将从磁盘缓存中获得的图片放到内存缓存存储一份 MemCache.put(md5Url, bitmap); iv.setImageBitmap(bitmap); return; } } catch (IOException e1) { e1.printStackTrace(); }
//从网络中下载 taskQueue.add(new Runnable() {
@Override public void run() { //下载任务 //发起网络连接,获得图像资源 try{ URL u = new URL(url); HttpURLConnection connection = (HttpURLConnection) u.openConnection(); connection.setRequestMethod("GET"); connection.setDoInput(true); connection.connect(); InputStream is = connection.getInputStream(); //要对图像进行压缩 Bitmap bitmap = compress(is,iv); is.close(); //将压缩后的图像放到内存缓存中存储 MemCache.put(md5Url, bitmap); //将压缩后的图像放到磁盘缓存中存储 Editor editor = DiskCache.edit(md5Url); OutputStream os = editor.newOutputStream(0); bitmap.compress(CompressFormat.JPEG, 100, os); editor.commit(); //写日志(可选操作) DiskCache.flush();
ValueObject vo = new ValueObject(iv, md5Url, bitmap);创建一个值类 Message.obtain(uiHandler, LOAD_FINISH, vo).sendToTarget(); //发消息说已经加载完了 }catch(Exception e){ e.printStackTrace(); } } }); Message.obtain(pollHandler, NEW_TASK).sendToTarget();//发消息说已经添加任务了 }
/** * 根据要显示的ImageView的大小对图像进行压缩 * @param is 图像源 * @param iv 未来要显示图像的ImageView * @return 压缩过后的图像 */
protected static Bitmap compress(InputStream is, ImageView iv) { Bitmap bitmap = null; try{
//1)先获得原始图像(图像源)的尺寸大小 //借助Optioins来获取图像的大小 //is----------byte[] ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buff = new byte[4096]; int len = -1; while((len=is.read(buff))!=-1){ out.write(buff, 0, len); }
Options opts = new Options(); opts.inJustDecodeBounds = true; //opts一旦设定了inJustDecodeBounds = true //BitmapFactory是不会返回bitmap对象,只会null BitmapFactory.decodeByteArray(out.toByteArray(),0, out.toByteArray().length, opts); //值放到opts中 int width = opts.outWidth;//图像的宽度(像素数) int height = opts.outHeight;//图像高度(像素数) //2)获取希望的宽度/高度 int targetWidth = iv.getWidth();//ImageView的宽度 int targetHeight = iv.getHeight();//Imageview的高度 //如果取ImageView宽度/高度取不到 if(targetHeight==0||targetWidth==0){ //可以手动指定值(80dp,100dp) //以当前设备屏幕的宽度/和高度来作为targetWiddth/targetHeight targetWidth = c.getResources().getDisplayMetrics().widthPixels; targetHeight = c.getResources().getDisplayMetrics().heightPixels; }
//3)计算压缩比 int sampleSize = 1;
if(width*1.0/targetWidth>1||height*1.0/targetHeight>1){ sampleSize = (int) Math.ceil(Math.max(width*1.0/targetWidth, height*1.0/targetHeight));//向上取整 }
//4)压缩图像 opts.inSampleSize = sampleSize; opts.inJustDecodeBounds = false; bitmap = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.toByteArray().length,opts); out.close(); }catch(Exception e){ e.printStackTrace(); } return bitmap; } private static String getMD5(String url) { String result="";
try { MessageDigest md = MessageDigest.getInstance("md5");//加密 md.update(url.getBytes()); byte[] bytes = md.digest(); StringBuilder sb = new StringBuilder(); for(byte b:bytes){ String str = Integer.toHexString(b & 0xFF); if(str.length()==1){ sb.append("0"); } sb.append(str); } result = sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return result; } private static int getNumberOfCores() { File file = new File("/sys/devices/system/cpu/"); File[] files = file.listFiles(new FilenameFilter() {
@Override public boolean accept(File dir, String filename) {
if(filename.matches("cpu[0-9]")){ return true; }else{ return false; } } });
if(files.length>0){ return files.length; }else{ return 1; }
}
private static class ValueObject{ ImageView iv;//发起下载任务的那个ImageView String url;//下载任务中要下载的那个地址 Bitmap bitmap;//url所对应的图片 public ValueObject(ImageView iv, String url, Bitmap bitmap) { super(); this.iv = iv; this.url = url; this.bitmap = bitmap; } } }
|