ImageLoader的原理

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.Image;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;

/**
 * 图片异步加载工具类:图片下载;添加给imageview
 * 线程池:限制并行线程数(允许一定数量的线程同时运行,其他线程处于等待阶段)
 * 图片异步加载时获取图片原理:1>Lru查找内存是否有本张图片->直接使用->下载成功过并且没有被清理
 *                     2>查找本地存储设备是否有本张图片->有:图片下载成功并且被lru清理;无:图片没有下载
 *                     3>通过网络连接下载图片
 *           ListView标志锁:作用当listview快速滑动(抛动)时,
 *                        防止加载多余(ListView快速滑动时划过的Item)的本地或网络图片      
 *           OnScrollListener->判断listView滑动状态
 * */
public class LoaderImg implements OnScrollListener{

    private int max_Size = 5;//线程池核心线程数->可以同时进行下载操作的线程
    private ExecutorService thread_pool;//线程池->限制线程马路
    //android.support.v4.util.LruCache->为了兼容低版本jar包
    //LruCache原理当存放value大小达到初始化时设置限定大小时进行清理内存操作
    int max_Mamory = (int) Runtime.getRuntime().maxMemory()/10;//应用程序程序运行时所使用最大内存数
    //每添加一条数据,返回添加数据的占用内存大小
    @SuppressLint("NewApi")
    private LruCache<String,Bitmap>lru = new LruCache<String, Bitmap>(max_Mamory){
        protected int sizeOf(String key, Bitmap value) {
            //返回添加图片的大小
            return value.getByteCount();
        };
    };//内存中存放网络下载图片容器
    //存储ImageView放置imageview对象覆盖
    List<ImageView>imgList = new ArrayList<ImageView>();
    boolean imgFlag = true;//是否向imglist中添加imageview标识

    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>滑动标志锁使用
    private boolean FlingFlag = true;//滑动标识(标志锁)
    private int firstPosion = 0;//第一条显示的索引
    private int visiblePosion = 0;//当前界面显示的条数
    private String urlPath;//图片前端地址
    private int bitmapID;//默认显示图片id
    private int flag;//默认标志
    private AbsListView lv;//listview

    //LoaderImg异步加载图片入口
    /**
     * name:图片名称;
     * urlPath:图片前段地址
     * img:要显示图片的控件
     * bitmapID:默认图片id
     * flag->getView是否滑动最后一条->imglist中添加
     * lv->正在显示的listview
     * */
    public void Load(String name,String urlPath,
            ImageView img,int bitmapID,int flag,AbsListView lv){
        if(name == null)
            return;
        if(urlPath == null)
            return;
        if(img == null)
            return;
        this.urlPath = urlPath;
        this.bitmapID = bitmapID;
        this.flag = flag;
        this.lv = lv;
        //img设置Tag在handler中判断下载完成图片与img要显示图片是否一致
        img.setTag(name);
        if(imgFlag){
            imgList.add(img);
        }
        if(flag == -1){
            imgFlag = false;
        }
        //设置默认显示图片
        img.setImageResource(bitmapID);
        //设置当前显示的listview滑动监听
        lv.setOnScrollListener(this);
        //启用滑动标志锁->不加载图片
        if(!FlingFlag)
            return;
        //1>Lru中查找
        Bitmap bitmap;
        bitmap = lru.get(name);
        if(bitmap != null){
            img.setImageBitmap(bitmap);
        }else{
            //2>Lru中没有本张图片->本地查找
            //3>网络下载图片
            if(thread_pool == null){
                //初始化线程池并且设置核心线程数->线程池中添加线程不需要使用start方法
                thread_pool = Executors.newFixedThreadPool(max_Size);
            }
            //线程加入线程池
            thread_pool.execute(new LoadThread(name, urlPath));
        }
    }

    //接收下载完成图片
    Handler hand = new Handler(){
        public void handleMessage(android.os.Message msg) {
            super.handleMessage(msg);
            if(msg.what == 200){
                //服务器下载回来图片
                Bitmap bitmap = (Bitmap) msg.obj;
                String name = msg.getData().getString("Name");
                if(name!=null&&bitmap!=null){
                    //添加到Lrucatch中(1级缓存中)
                    lru.put(name, bitmap);
                    //添加到本地存储中(2级缓存)
                    //图片添加给ImageView显示
                    //判断ImageView对应显示图片->Tag判断->规避:图片错位->原因:图片复用机制引起
                    //缓存到imgList中的Imageview获取
                    ImageView img = null;//list中获取要显示下载完成图片的imageview
                    for(int i = 0;i < imgList.size();i++){
                        String tag = (String) imgList.get(i).getTag();
                        if(name.equals(tag)){
                            img = imgList.get(i);
                            break;
                        }
                    }
                    if(img != null)
                        //网络获取图片添加给对应imageview
                        img.setImageBitmap(bitmap);
                }
            }
        };
    };

    private class LoadThread implements Runnable{

        private String name;//图片名称
        private String urlPath;//图片地址

        public LoadThread(String name,String urlPath){
            this.name = name;
            this.urlPath = urlPath;
        }

        @Override
        public void run() {
            try {
                URL url = new URL(urlPath+name);
                HttpURLConnection httpc = (HttpURLConnection) url.openConnection();
                httpc.setConnectTimeout(60*1000);
                httpc.setReadTimeout(60*1000);
                httpc.setDoInput(true);
                if(httpc.getResponseCode() == 200){
                    InputStream in = httpc.getInputStream();
                    Bitmap resultBitmap = BitmapFactory.decodeStream(in);
                    Message msg = hand.obtainMessage();
                    msg.what = 200;
                    msg.obj = resultBitmap;
                    Bundle bund = new Bundle();
                    bund.putString("Name", name);
                    msg.setData(bund);//封装图片名称
                    hand.sendMessage(msg);
                }
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 

        }
    }


    //当listview停止滚动时刷新item中imageview方法
    private void RefrashItem(){
        //获取ImageView出来->二次调用load()
        visiblePosion = visiblePosion+1;
        Log.e("",""+visiblePosion);
        if(imgList.size()<5)
            visiblePosion = imgList.size();
        for(int i = (imgList.size()-1);i>=(imgList.size()-visiblePosion);i--){
            ImageView img = imgList.get((i));
            Log.e("", "name:"+(String)img.getTag());
            Load((String)img.getTag(), 
                    urlPath, img,
                    bitmapID, flag, lv);
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState == OnScrollListener.SCROLL_STATE_IDLE){
            //停止->加载图片->打开滑动标志锁
            FlingFlag = true;
            //刷新界面
            RefrashItem();
        }else if(scrollState == OnScrollListener.SCROLL_STATE_FLING){
            //抛动时->不加载图片->FlingFlag= false;->添加滑动标志锁
            FlingFlag = false;
        }else{
            //滑动时->打开滑动标志锁
            FlingFlag = true;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        firstPosion = firstVisibleItem;
        visiblePosion = visibleItemCount;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值