Android从网上异步加载图片到ListView注意事项

利用AsyncTask启动一个异步任务,在doInBackground方法中,根据图片的地址下载图像。当图像下载完毕后会将图像作为方法的返回结果提交到onPostExecute方法的参数中。
在onPostExecute方法中将该图像放到ImageView中进行显示。
public static void loadImage(final String url,final ImageView iv){
        new AsyncTask<Void, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(Void... params) {
                try {
                    URL ur = new URL(url);
                    HttpURLConnection connection = (HttpURLConnection) ur.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setDoInput(true);
//                    connection.setDoOutput(true);
                    connection.connect();
                    InputStream is = connection.getInputStream();
                    //将二进制流转换为图片
                    Bitmap bitmap = BitmapFactory.decodeStream(is);
                    is.close();
                    return bitmap;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
            
            //根据返回结果判断图片(头像)的设定值
            @Override
            protected void onPostExecute(Bitmap result) {
                if (result==null) {
                    iv.setImageResource(R.drawable.cover_default);
                }
                iv.setImageBitmap(result);
            }
        }.execute();
        
    }
当简单加载图像方式遇到了ListView可能产生的问题:1)ListView的条目复用机制导致的图片加载的“动画”效果2)随着ListView滚动,后台会启动大量的异步任务,影响程序的流畅度3)下载图像时不能直接使用,而需要进行压缩处理4)使用缓存技术来解决图片的不断重复加载。只有在迫不得已的情况下,才要去网络中获取图像

生产者与消费者关系模型
解决上面的问题,就是在ListView与下载任务队列间添加缓存程序


以下程序使用lruCache 和disclruCache分别完成了就判断图片是不是在内存中和磁盘中含有 ,有的化就不会再从网上加载,也不会在上下滑动ListView时出现图片的闪烁现象

package com.example.music.util;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;

import com.example.music.util.DiskLruCache.Editor;
import com.example.music.util.DiskLruCache.Snapshot;

public class ImageLoader {
    //线程池中线程数量
    //(这个数量应该与设备CPU的核数相同)
    public static int Thread_Count;
    public static Context c;//上下文
    //内存缓存
    public static LruCache<String , Bitmap> MemCache;
    //(双向任务队列)
    //未来可以根据需求决定选择FIFO或LIFO
    public static LinkedBlockingDeque<Runnable> taskQuen;
    //线程池
    public static ExecutorService exec;
    //从任务队列中取下载任务的handler
    public static Handler pollHandler;
    //任务下载完毕后,用来刷星ListView条目显示下载图片
    public static Handler uiHandler;
    //“养活”pollHandler
    public static Thread pollThread;
    //起到一个标示的作用:用来确保ImageLoader只要进行一次初始化就行
    public static boolean isFirstTime = true;
    
    public static final int LOAD_FINISH = 101;//加载完返回
    public static final int NEW_TASK = 102;//有新的任务
    
    //磁盘缓存
    public static DiskLruCache DiskSCache;
    
    /**
     * 初始化所有imageloader属性值*/
    public static void init(Context context){
        if (!isFirstTime) {
            return;
        }
        
        c = context;
        //初始化
        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.getWidth();
            }
        };
        //初始化磁盘缓存
        try {
            DiskSCache = DiskLruCache.open(directory(), appVersion(), 1, 1024*1024*10);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        
        //初始化队列
        taskQuen = new LinkedBlockingDeque<Runnable>();
        exec = Executors.newFixedThreadPool(Thread_Count);
        //TODO pollHandler uiHandler
        uiHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                if (msg.what==LOAD_FINISH) {
                    //TODO 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(){//普通线程,要想有pollHandler,就要先具有Looper.prepare();Looper.loop();使线程能够跑起了
            public void run() {
                Looper.prepare();
                pollHandler = new Handler(){
                    public void handleMessage(Message msg) {
                        if (msg.what==NEW_TASK) {
                            try {
                                Runnable task = taskQuen.takeFirst();
                                //将取出的任务放到线程池中
                                exec.execute(task);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }else{
                            super.handleMessage(msg);
                        }
                    }
                };
                Looper.loop();//死循环,有关代码要写在其上面Looper.prepare()下面
            }
        };
        pollThread.start();
        isFirstTime = false;
    }
    
    private static int appVersion() {
        
        try {
            PackageInfo infor = c.getPackageManager().getPackageInfo(c.getPackageName(), 0);
            return infor.versionCode;
        } catch (Exception e) {
            e.printStackTrace();
        }        
        return 1;
    }

    private static File directory() {
        
        String path  = c.getCacheDir().getPath();
        return new File(path,"imageloadercache");
    }

    /**获取URL指向的图片放到ListView中显示
     * url 图像路径
     * iv 需要被显示的图片
     * */
    public static void loadImage(final String url, final ImageView iv){
        if (isFirstTime) {
            throw new RuntimeException("ImageLoader未被初始化");
        }
        //先判断,url所指向的图像是否在缓存中有缓存
        //url转为MD5格式的字符串       MD5常用于加密操作(只能正推,不能反推)
        final String md5Url = getMD5(url);
        //设置tag,到时候要与vo中的url进行对比
        
        iv.setTag(md5Url);
        
        Bitmap bitmap = MemCache.get(url);
        if (bitmap!=null) {
            Log.d("TAG", "图像是从内存中加载的");
            iv.setImageBitmap(bitmap);
            return;
        }
        
        try {
            Snapshot snap = DiskSCache.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();
        }
        
        //从网络中下载
        taskQuen.add(new Runnable() {
            @Override
            public void run() {
                //TODO 进行网络下载
                //进行下载任务
                //发起网络连接,获取图片资源
                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);//根据IV的大小为依据进行压缩
                    
                    is.close();
                    //放到内存中进行存储
                    MemCache.put(md5Url, bitmap);
                    
                    //将压缩后的图片放到磁盘缓存中存储
                    Editor editor = DiskSCache.edit(md5Url);
                    OutputStream os = editor.newOutputStream(0);
                    bitmap.compress(CompressFormat.JPEG, 100, os);        
                    editor.commit();
                    DiskSCache.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();
    }
    /**根据要显示image的大小对图像进行压缩
     *
     * is图像源
     * iv */
    protected static Bitmap compress(InputStream is, ImageView iv) {
        Bitmap bitmap = null;
        try {
            //1)获得图像源的大小
            //借助Optionins来获取图片的大小(不下载图片的情况下)
            //is先转为byte数组
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buff = new byte[4096];
            int len = -1;
            while((len = is.read(buff))!=-1){
                bos .write(buff, 0, len);
            }
            //2)获得图片的大小和默认显示的大小
            Options opts = new Options();
            opts.inJustDecodeBounds = true;//
            BitmapFactory.decodeByteArray(bos.toByteArray(), 0, bos.toByteArray().length,opts);//设置opts.inJustDecodeBounds = true以后没有任何返回值 ,只会返回null    得到的值在optsz中
            int width = opts.outWidth;
            int height = opts.outHeight;
            
            int targetWidth = iv.getWidth();
            int targetHeight = iv.getHeight();
            //targetWidth、targetHeight两个值有可能为零时 ,这时就取不到宽高了
            if (targetHeight == 0 || targetWidth == 0) {
                //这时可以手动指定一个值或者以设备的宽高为目标值
                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));//向上取整ceil宁愿使图片大点
                
            }
            //4)压缩图像
            opts.inSampleSize = sampleSize;
            opts.inJustDecodeBounds = false;//设置为false,可以是下面的代码有返回值
            bitmap = BitmapFactory.decodeByteArray(bos.toByteArray(),0, bos.toByteArray().length,opts);
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    /**将地址转换为MD5字符串格式*/
    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 (Exception 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;
        String  url;
        Bitmap bitmap;
        public ValueObject(ImageView iv, String url, Bitmap bitmap) {
            super();
            this.iv = iv;
            this.url = url;
            this.bitmap = bitmap;
        }
        
    }
    
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值