加载图片工具类

本文介绍了一个基于HandlerThread的图片加载器实现方案,该方案利用线程池和LRU缓存来提高图片加载效率,并支持从网络和本地加载图片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用HandlerThread实现的加载,HandlerThread中定义了线程、调用了Looper.prepare()和Looper.loop()方法。与Handler绑定后,loop()方法循环从MessageQueue中取Handler发送来的消息进行图片加载。

直接上代码:

/**
 * 加载图片核心类,可以拷贝直接用
 * @Description: TODO
 * @author 张永飞
 * @date 2016-6-11 
 *
 */
public class ImageLoader {

    private static ImageLoader mInstance;
    // 队列调度方式: 后进先出
    public static final int LIFO = 0;
    // 队列调度方式: 先进先出
    public static final int FIFO = 1;
    private Handler mThreadHandler;
    // 队列调度方式
    private int mType;
    // 线程池
    private ExecutorService mThreadPool;
    // 控制线程资源的信号量
    private volatile Semaphore mPoolSemaphore;
    // 任务队列链表
    private LinkedList<Runnable> mTaskQueue;
    private LruCache<String, Bitmap> mLruCache;
    private Handler mUIHandler;
    // 是否开启本地磁盘缓存
    private static final boolean isDiskCacheEnable = true;

    public static ImageLoader getInstance(int threadCount, int type) {
        if (mInstance == null) {
            synchronized (ImageLoader.class) {
                if (mInstance == null) {
                    mInstance = new ImageLoader(threadCount, type);
                }
            }
        }
        return mInstance;
    }

    private ImageLoader(int threadCount, int type) {
        initBackThread();
        initLruCache();
        mType = type;
        mTaskQueue = new LinkedList<Runnable>();
        mThreadPool = Executors.newFixedThreadPool(threadCount);
        mPoolSemaphore = new Semaphore(threadCount);
    }

    private void initBackThread() {
        HandlerThread handlerThread = new HandlerThread("image-loader-thread");
        handlerThread.start();
        mThreadHandler = new Handler(handlerThread.getLooper()) {
            public void handleMessage(Message msg) {
                try {
                    mPoolSemaphore.acquire();
                    mThreadPool.execute(getTask());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        };
    }

    private void initLruCache() {
        // 进程可以从虚拟机获取的最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        mLruCache = new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // 图片的行字节数 * 图片的高度 = 图片的字节数
                return value.getRowBytes() * value.getHeight();
            }
        };
    }

    /**
     * 根据路径设置图片到对应的ImageView上
     * @param path 图片全路径名,可以是URL
     * @param imageView 显示图片的控件
     * @param isFromNet 是否从网络下载
     */
    public void loadImage(final String path, final ImageView imageView,
            final boolean isFromNet) {
        imageView.setTag(path);
        if (mUIHandler == null) {
            mUIHandler = new Handler() {
                public void handleMessage(Message msg) {
                    ImageBeanHolder holder = (ImageBeanHolder) msg.obj;
                    ImageView imageView2 = holder.imageView;
                    Bitmap bitmap2 = holder.bitmap;
                    String path2 = holder.path;
                    // 防止图片错位
                    if (imageView2.getTag().toString().equals(path2)) {
                        imageView2.setImageBitmap(bitmap2);
                    }
                };
            };
        }

        Bitmap bitmap = getBitmapFromLruCache(path);
        if (bitmap != null) {
            sendMsgToUIHandler(path, imageView, bitmap);
        } else {
            addTask(buildTask(path, imageView, isFromNet));
        }
    }

    private synchronized void addTask(Runnable runnable) {
        mTaskQueue.add(runnable);
        mThreadHandler.sendEmptyMessage(0);
    }

    private synchronized Runnable getTask() {
        Runnable result = null;
        if (mType == LIFO) {
            result = mTaskQueue.removeLast();
        } else if (mType == FIFO) {
            result = mTaskQueue.removeFirst();
        }
        return result;
    }

    private Runnable buildTask(final String path, final ImageView imageView,
            final boolean isFromNet) {
        return new Runnable() {

            @Override
            public void run() {
                Bitmap bitmap = null;
                if (isFromNet) {
                    bitmap = loadImageFromUrl(path, imageView);
                } else {
                    bitmap = loadImageFromLocal(path, imageView);
                }
                putBitmapToLruCache(path, bitmap);
                sendMsgToUIHandler(path, imageView, bitmap);
                mPoolSemaphore.release();
            }
        };
    }

    private Bitmap loadImageFromUrl(String path, ImageView imageView) {
        File picDiskCacheFile = getPicDiskCacheFile(imageView.getContext(),
                getFileMD5(path));
        Bitmap bitmap = null;
        if (picDiskCacheFile.exists()) {
            bitmap = loadImageFromLocal(picDiskCacheFile.getAbsolutePath(),
                    imageView);
        } else {
            if (isDiskCacheEnable) {
                boolean isSuccess = downloadUrlPic(path, picDiskCacheFile);
                if (isSuccess) {
                    bitmap = loadImageFromLocal(
                            picDiskCacheFile.getAbsolutePath(), imageView);
                } else {
                    System.out.println(picDiskCacheFile.getAbsolutePath()
                            + "下载失败,删除");
                    picDiskCacheFile.delete();
                }
            } else {
                bitmap = loadUrlBitmap(path, imageView);
            }
        }
        return bitmap;
    }

    private Bitmap loadImageFromLocal(String path, ImageView imageView) {
        ImageViewSize imageViewSize = getImageViewSize(imageView);
        return decodeThumbnailFromResource(path, imageViewSize);
    }

    // 从网络下载图片
    private boolean downloadUrlPic(String urlStr, File file) {
        FileOutputStream fos = null;
        InputStream is = null;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            is = conn.getInputStream();
            fos = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            fos.flush();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private String getFileMD5(String path) {
        try {
            MessageDigest digest = MessageDigest.getInstance("md5");
            byte[] bytes = digest.digest(path.getBytes());
            StringBuilder result = new StringBuilder();
            String tempStr = null;
            for (byte b : bytes) {
                tempStr = Integer.toHexString(b & 0xff);
                if (tempStr.length() == 1) {
                    result.append("0");
                }
                result.append(tempStr);
            }
            return result.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    private File getPicDiskCacheFile(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    // 从网络获取用于显示在ImageView中的图片
    private Bitmap loadUrlBitmap(String urlStr, ImageView imageView) {
        InputStream is = null;
        try {
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(conn.getInputStream());
            is.mark(is.available());
            
            Options options = new Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, options);
            ImageViewSize imageViewSize = getImageViewSize(imageView);
            options.inSampleSize = getInSampleSize(imageViewSize, options);
            options.inJustDecodeBounds = false;
            is.reset();
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
            conn.disconnect();
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void sendMsgToUIHandler(final String path,
            final ImageView imageView, Bitmap bitmap) {
        ImageBeanHolder holder = new ImageBeanHolder();
        holder.bitmap = bitmap;
        holder.imageView = imageView;
        holder.path = path;
        Message message = Message.obtain();
        message.obj = holder;
        mUIHandler.sendMessage(message);
    }

    private void putBitmapToLruCache(String path, Bitmap bitmap) {
        if (getBitmapFromLruCache(path) == null) {
            if (bitmap != null) {
                mLruCache.put(path, bitmap);
            }
        }
    }

    private Bitmap getBitmapFromLruCache(String path) {
        return mLruCache.get(path);
    }

    private Bitmap decodeThumbnailFromResource(String path,
            ImageViewSize imageViewSize) {
        Options options = new Options();
        // 不把位图加载到内存中,只获取位图的头文件信息
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        int inSampleSize = getInSampleSize(imageViewSize, options);
        options.inSampleSize = inSampleSize;
        // 把位图解析到内存中
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);
    }

    private int getInSampleSize(ImageViewSize imageViewSize, Options options) {
        int imageViewWidth = imageViewSize.width;
        int imageViewHeight = imageViewSize.height;
        int picWidth = options.outWidth;
        int picHeight = options.outHeight;
        int inSampleSize = 1;
        if (picWidth > imageViewWidth || picHeight > imageViewHeight) {
            int widthScale = Math.round(picWidth * 1.0f / picHeight);
            int heightScale = Math.round(picHeight * 1.0f / imageViewHeight);
            inSampleSize = Math.max(widthScale, heightScale);
        }
        return inSampleSize;
    }

    private ImageViewSize getImageViewSize(ImageView imageView) {
        ImageViewSize imageViewSize = new ImageViewSize();
        DisplayMetrics metrics = imageView.getContext().getResources()
                .getDisplayMetrics();
        LayoutParams params = imageView.getLayoutParams();
        int width = imageView.getWidth();// imageView的实际宽度
        if (width <= 0) {
            width = params.width;// layout中声明的宽度
        }
        if (width <= 0) {
            width = imageView.getMaxWidth();// imageView的最大宽度
        }
        if (width <= 0) {
            width = metrics.widthPixels;// 屏幕宽度
        }
        int height = imageView.getHeight();
        if (height <= 0) {
            height = params.height;
        }
        if (height <= 0) {
            height = imageView.getMaxHeight();
        }
        if (height <= 0) {
            height = metrics.heightPixels;
        }
        imageViewSize.width = width;
        imageViewSize.height = height;
        return imageViewSize;
    }

    private static class ImageBeanHolder {
        Bitmap bitmap;
        ImageView imageView;
        String path;
    }

    private static class ImageViewSize {
        int width;
        int height;
    }

}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值