图片加载

一.简单异步加载图片的方式(在listview中显示从网络下载的图片)

利用AsyncTask启动一个异步任务,在doInBackground方法中,根据图片的地址下载图像。当图像下载完毕后会将图像作为方法的返回结果提交到onPostExecute方法的参数中。在onPostExecute方法中将该图像放到ImageView中进行显示。

应用场合:图片加载量比较小的场合,可以使用这种方式进行加载。

当简单加载图像方式遇到了ListView可能产生的问题:
1)ListView的条目复用机制导致的图片加载的“动画”效果
2)随着ListView滚动,后台会启动大量的异步任务,影响程序的流畅度
3)下载图像时不能直接使用,而需要进行压缩处理
4)使用缓存技术。只有在迫不得已的情况下,才要去网络中获取图像
二.生产者消费者模型在listview中加载图片
这里写图片描述

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.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

import com.example.mymusicplayers.Util.DiskLruCache.Editor;
import com.example.mymusicplayers.Util.DiskLruCache.Snapshot;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
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.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

public class ImageLoader {
    public static int Thread_Count;//线程池中线程的数量,应该与设备cpu的合数相同
    public static Context c;//上下文
    public static LruCache<String, Bitmap> MemCache;//内存缓存
    public static LinkedBlockingDeque<Runnable> taskQueue;//双向队列可以根据需求选择后进先出,后加载的任务是用户最想要看到的任务,listview最下面的任务;
    public static ExecutorService exec;//线程池
    public static Handler pollHandler;//从任务队列中取下载任务的handler
    public static Handler uiHandler;//任务下载完成之后,用来刷新listview条目的handler
    public static Thread pollThread;//取下载任务的handler要依靠这个线程来存活;
    public static boolean isFirstTime=true;//标示作用,用来保证imageloader只执行一次,进行一次初始化; 
    public static final int LOAD_FINISH=101;//
    public static final int NEW_TASK =102;
    public static  DiskLruCache diskache;//磁盘缓存

    public  static void init(Context context){
        if(!isFirstTime){
            return;
        }       
        c=context;
        //获得线程数,机器是几核就起几个线程;
        Thread_Count=getNunberOfCores();

        //初始化内存缓存;
        MemCache=new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory()/8)){
            protected int sizeOf(String key, Bitmap value) {
                return value.getHeight()*value.getWidth();          
            };
        };
       //设置磁盘缓存;
        try {                          //存储目录,    //程序版本号.智能清理旧版本的信息
            diskache = DiskLruCache.open(directory(), appVersion(), 1, 1024*1024*10);
        } catch (IOException e1) {                        //一个k对应几个value//分配的磁盘缓存从大小;
            e1.printStackTrace();
        }

        //初始化任务队列
        taskQueue=new LinkedBlockingDeque<Runnable>();

        //初始化线程池有固定线程数量的线程池;
           //后面加s的一般都是工具类;
        exec=Executors.newFixedThreadPool(Thread_Count); 
        //初始化ui线程的handler;
        uiHandler=new Handler(Looper.getMainLooper()){
            public void handleMessage(Message msg) {
                //TODO 不理解;
                //加载完毕,通知ui线程 进行更新图片;
                if(msg.what==LOAD_FINISH){
                    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(){
            public void run() {
                //创建一个looper
                Looper.prepare();
                //在工作线程起一个handler;
                pollHandler=new Handler(){
                    public void handleMessage(Message msg) {
                        //工作线程的handler,有新的任务,把新的任务从消息队列中取出放入线程池;
                        if(msg.what==NEW_TASK){
                            //现在任务队列中放入了新的下载任务,
                            //取任务队列中取任务;
                            try {
                                Runnable task = taskQueue.takeFirst();//从队列中取出一个任务,
                                //将取出来的任务放到线程池中;
                                exec.execute( task);                            
                            } catch (InterruptedException e) {                          
                                e.printStackTrace();
                            }
                        }else{
                            super.handleMessage(msg);
                        }
                    }
                };
                //TODO   不理解;
                Looper.loop();
            }
        };

        pollThread.start();
        Log.i("TAG", "isFirstTime>>>>>>>>>1>>>>>>>>>>>>>>>>>>>>>>"+isFirstTime);
        isFirstTime=false;
    }
    private static File directory() {
        String path = c.getCacheDir().getPath();
        return new File(path,"imageLoadercache");
    }
    private static int appVersion() {
        try {
            PackageInfo info = c.getPackageManager().getPackageInfo(c.getPackageName(), 0);//0表示没有附加属性;
            return info.versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }
    //获取url指向的图像,放到iv中显示;
    public static void loadimage(final String url,final ImageView iv){
        Log.i("TAG", "isFirstTime>>>>>>>>>2>>>>>>>>>>>>>>>>>>>>>>"+isFirstTime);
        if(isFirstTime){
            throw new RuntimeException("imageloader未初始化");  
        }
        //先判断url所指向的图像是否在缓存中有保存;
        //url转换成MD5格式的字符串;MD5:将任意长度的格式的转换成相同长度的字符串; 
        final String md5url=getMD5(url);

        iv.setTag(md5url);

        Bitmap bitmap=MemCache.get(md5url);
        if(bitmap!=null){
            Log.d("TAG","图像时从缓存中加载的");
            iv.setImageBitmap(bitmap);
            return;
        }   
        try {
            Snapshot snap = diskache.get(md5url);
            if(snap!=null){
                Log.i("TAG", "头像是从磁盘中获取的");
                InputStream in=snap.getInputStream(0);//一个key对应一个值,取下标为0的位置的值;
                bitmap=BitmapFactory.decodeStream(in);//把一个流对象编译成一个bitmap;
                //把bitmap放入到内存中,
                MemCache.put(md5url, bitmap);
                iv.setImageBitmap(bitmap);

                return;
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        }

       //如果缓存,内存都没有,从服务器下载,把这个任务添加到任务队列中,
        taskQueue.add(new Runnable() {
            public void run() {
                try {
                    URL u=new URL(url);
                    HttpURLConnection conn=(HttpURLConnection) u.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setDoInput(true);
                    conn.connect();
                    InputStream in = conn.getInputStream();
                    //要对图片进行压缩;
                    Bitmap bitmap=compress(in,iv);//根据iv的大小来压缩图像
                    in.close();
                    MemCache.put(md5url,bitmap);


                    //将压缩后的图片放到磁盘中
                    Editor editor=diskache.edit(md5url);
                    OutputStream os = editor.newOutputStream(0);
                    bitmap.compress(CompressFormat.JPEG, 100, os);//100表示不压缩,上面已经手动压缩过了,直接存在磁盘中就可以;
                    editor.commit();//提交
                    diskache.flush();//写日志;(是可选操作);

                    valueObject vo=new valueObject(iv, md5url, bitmap);
                    //UI线程提醒加载完毕,需要刷新了,
                    Message.obtain(uiHandler,LOAD_FINISH,vo).sendToTarget();


                } catch (Exception e) {

                    e.printStackTrace();
                }
            }
        });
        //工作线程提醒有新的任务,
        Message.obtain(pollHandler,NEW_TASK).sendToTarget();

    }
    /**
     * 根据要显示的iv的大小,对图像进行压缩
     * in 图像源
     * iv 要显示图像的imageview
     * 返回压缩过后的图像
     * 
     * */
    protected static Bitmap compress(InputStream in, ImageView iv) {
        Bitmap bitmap=null;
        try{
            //先获得原始图像的尺寸大小;
            //借助options来获得图像的大小
            //in>>>>>byte[]
            ByteArrayOutputStream out=new ByteArrayOutputStream();
            byte[] buff=new byte[4096];
            int len=-1;
            while((len=in.read(buff))!=-1){
                out.write(buff,0,len);//注意不能写buff.length;//
            }
            //获得原始图像的宽高
            Options opts=new Options();//用来原始图像的宽和高;
            opts.inJustDecodeBounds=true;//如果为true;下面的bm没有任何返回值,为空,只有opts中有数据

            Bitmap bm=BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.toByteArray().length, opts);//没有任何返回值,为空,只有opts中有数据//
            //  bitmap bm=BitmapFactory.decodeByteArray(data, offset, length, opts)

            int width=opts.outWidth;//原始图像在宽高上的像素数
            int height=opts.outHeight;

            //要显示图像的控件的宽高,
            int targetwidth=iv.getWidth();
            int targetheight=iv.getHeight();
              //计算量大的时候,还没来的及算出宽高,就开到执行过去了,
            if(targetwidth==0||targetheight==0){
                //手动设置指定的值,(80dp,100dp)
                //或者设置为屏幕的宽高,
                targetwidth=c.getResources().getDisplayMetrics().widthPixels;//设备的宽;
                targetheight=c.getResources().getDisplayMetrics().heightPixels;//设备的高;
                /*targetheight=80;
         targetwidth=80;*/
            }
            //计算压缩比

            int  samplesize=1;
            if(width*1.0/targetwidth>1||height*1.0/targetheight>1){//*1.0的目地是使运算结果为double类型而不是整数
                samplesize=(int)Math.ceil(Math.max(width*1.0/targetwidth,height*1.0/targetheight));
            }
            //压缩图片
            opts.inSampleSize=samplesize;
            opts.inJustDecodeBounds=false;
            bitmap=BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.toByteArray().length, opts);
            in.close();


        }catch(Exception e){
            e.printStackTrace();
        }
        return bitmap;
    }
    private static String getMD5(String url) {
        String result="";
        try {
            MessageDigest md=MessageDigest.getInstance("md5");//使用md5方法进行加速;
            md.update(url.getBytes());//把字符串转换成byte数组;

            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 getNunberOfCores() {
        //找到表示cpu核数的路径
        File file=new File("/sys/devices/system/cpu");
        //过滤有几个"cpu+数"字的文件;
        File[] files=file.listFiles(new FilenameFilter() {
            //过滤文件,检查cpu是几核;有cpu0,cpu1,cpu2等,cpu后面gen数字,如果后面不是数字,都与cpu的核数无关;,有几个这样的文件,就有几核,可以起几个线程;
            @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;//发起下载任务的imageviw
        String url;
        Bitmap bitmap;
//此类的作用是保证加载的图片不闪图
        public valueObject(ImageView iv, String url, Bitmap bitmap) {
            super();
            this.iv = iv;
            this.url = url;
            this.bitmap = bitmap;
        }   
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值