Android基础控件之图片加载(带缓存)

为了防止老年痴呆,基础的东西也要记一记。
其实图片加载用的框架很多。但是简单的加载个图片还是直接用系统的api处理一下比较节约了。
ImageView的setImageBitmap方法和异步加载就不说了。就来看一下bitmap的获取。

最基本的方法如下:

public static Bitmap returnBitMap(String url,int _w,int _h) {
        URL myFileUrl = null;
        Bitmap bitmap = null;
        try {
            myFileUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection();
            conn.setDoInput(true);
            conn.connect();
            InputStream is = conn.getInputStream();
            bitmap = BitmapFactory.decodeStream(is);
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

很简单吧,然而实际使用中如果只是这样的话,分分钟OOM给你看哦。
那么,OOM错误的原因就是图片如果很多或者很大,内存就溢出了。万幸我们可以选择输出多大的尺寸的Bitmap,而通常,一个App中的大部分图片其实是很小的。(如果都很大那就要涉及回收的问题了,这里不展开)

所以我们要控制一下获取的Bitmap的大小。

先上代码:

public static Bitmap returnBitMap(String url,int _w,int _h) {
        URL myFileUrl = null;
        Bitmap bitmap = null;
        try {
            myFileUrl = new URL(url);
            //获得缩放比例
            HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection();
           // conn.setConnectTimeout(3000);
            conn.setDoInput(true);
            conn.connect();
            InputStream is = conn.getInputStream();
            BitmapFactory.Options _options = new BitmapFactory.Options();
            //设成true就只返回宽高
            _options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is,null,_options);
            int scaleSize=1;
            if(_w>0 && _h>0){
                //缩略图的比例
                scaleSize=calculateInSampleSize(_options,_w,_h);
                if(scaleSize<1){
                    scaleSize=1;
                }
            }
            conn.disconnect();
            is.close();
            //获得实际的图片数据
            BitmapFactory.Options options = new BitmapFactory.Options();
            //不涉及透明度可使用RGB_565
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            options.inJustDecodeBounds = false;
            options.inSampleSize=scaleSize;
            conn = (HttpURLConnection) myFileUrl.openConnection();
            //conn.setConnectTimeout(3000);
            conn.setDoInput(true);
            conn.connect();
            is = conn.getInputStream();
            bitmap = BitmapFactory.decodeStream(is,null,options);
            //对位图的宽度进行缩放
            try{
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                Bitmap newBitmap;
                if(width>0 && height>0 && _w>0 && _h>0) {
                    float scaleWidth = ((float) bitmap.getHeight() * _w / _h) / bitmap.getWidth();
                    Matrix matrix = new Matrix();
                    matrix.postScale(scaleWidth, 1);
                    newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
                    is.close();
                    return newBitmap;
                }
            }catch (Exception e){
            }
            is.close();
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

/**
     * 计算缩放量
     */
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

主要就在BitmapFactory.Options部分。通过BitmapFactory.Options类可以获取原始图片的尺寸。然后与设备上实际的尺寸进行比较就可以得到需要的缩放比例。
先将inJustDecodeBounds属性设置成true,然后获取图片的bitmap,此时并不加载实际的图片内容,也就不占用内存,只是返回图片的原始尺寸。
然后根据图片在设备上显示的实际尺寸计算出缩放比例。
就是这里的calculateInSampleSize方法:

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

获取比例后将options.inJustDecodeBounds属性设置为false再次加载图片,并将缩放比例设置给options.inSampleSize属性就可以获得符合实际显示需要的尺寸,这就没有什么资源上的浪费了。
至于options.inPreferredConfig = Bitmap.Config.RGB_565;什么的就看具体需要了。

好,图片加载完了,再来个缓存吧。

毕竟每次显示都去请求一遍图片,想想就很闹心啊。
来,先来一个MemoryCache类保存图片

import android.graphics.Bitmap;

import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 图片缓存(其中的id是判断是否同一个图片的标识)
 */
public class MemoryCache {
    private Map> cache= Collections.synchronizedMap(new HashMap>());//软引用
    public Bitmap get(String id){
        if(!cache.containsKey(id))
            return null;
        SoftReference ref=cache.get(id);
        return ref.get();
    }
    
    public void put(String id, Bitmap bitmap){
        cache.put(id, new SoftReference<>(bitmap));
    }

    public void remove(String id){
        cache.remove(id);
    }

    public void clear() {
        cache.clear();
    }
}

然后获取图片的时候判断一下,缓存里有就直接取缓存的,没有就还是老样子。

        Bitmap bitmapCache = imgCache.get(url);//这里是以图片地址作为标识,当然也可以设别的ID
        if (bitmapCache != null && iscui) {
            int width = bitmapCache.getWidth();
            int height = bitmapCache.getHeight();
            Bitmap cache2NewBitmap;
            if(width>0 && height>0 && _w>0 && _h>0) {
                float scaleWidth = ((float) bitmapCache.getHeight() * _w / _h) / bitmapCache.getWidth();
                Matrix matrix = new Matrix();
                matrix.postScale(scaleWidth, 1);
                cache2NewBitmap = Bitmap.createBitmap(bitmapCache, 0, 0, width, height, matrix, true);
                return cache2NewBitmap;
            }
        }

图片获取完要存到cache里:

if(imagekeyList.size()>cacheMaxSize) {//超出数量就从最开头起删除
    imgCache.remove(imagekeyList.get(0));
    imagekeyList.remove(0);
}
imagekeyList.add(url);
imgCache.put(url, bitmap);

这里头的imagekeyListcacheMaxSize用来控制缓存数量

private static List imagekeyList=new ArrayList();
private static final int cacheMaxSize=100;

来,完整代码再来一遍:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 图片加载类
 */
public class imageLoader {
    private static MemoryCache imgCache = new MemoryCache();
    private static List imagekeyList=new ArrayList();
    private static final int cacheMaxSize=100;
    public static Boolean iscui=true;//是否加载缓存
    public static Bitmap returnBitMap(String url, int _w, int _h) {
        //检测是否已在内存
        Bitmap bitmapCache = imgCache.get(url);
        if (bitmapCache != null && iscui) {
            int width = bitmapCache.getWidth();
            int height = bitmapCache.getHeight();
            Bitmap cache2NewBitmap;
//            Log.d("LOGCAT","scale:"+width+"-"+height+"-"+_w+"-"+_h);
            if(width>0 && height>0 && _w>0 && _h>0) {
                float scaleWidth = ((float) bitmapCache.getHeight() * _w / _h) / bitmapCache.getWidth();
                Matrix matrix = new Matrix();
                matrix.postScale(scaleWidth, 1);
                cache2NewBitmap = Bitmap.createBitmap(bitmapCache, 0, 0, width, height, matrix, true);
                return cache2NewBitmap;
            }
        }
        URL myFileUrl = null;
        Bitmap bitmap = null;
        try {
            myFileUrl = new URL(url);
            //获得缩放比例
            HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection();
           // conn.setConnectTimeout(3000);
            conn.setDoInput(true);
            conn.connect();
            InputStream is = conn.getInputStream();
            BitmapFactory.Options _options = new BitmapFactory.Options();
            //设成true就只返回宽高
            _options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is,null,_options);
//            Log.d("LOGCAT","picSize:"+options.outWidth+"-"+options.outHeight+"-"+_w+"-"+_h);
            int scaleSize=1;
            if(_w>0 && _h>0){
                //缩略图的比例
                scaleSize=calculateInSampleSize(_options,_w,_h);
                if(scaleSize<1){
                    scaleSize=1;
                }
            }
            conn.disconnect();
            is.close();
            //获得实际的图片数据
            BitmapFactory.Options options = new BitmapFactory.Options();
            //不涉及透明度可使用RGB_565
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            //设置成内存不足时可回收
            options.inPurgeable = true;
            //设施是否深拷贝inPurgeable=false时无效
            options.inInputShareable = true;
            options.inJustDecodeBounds = false;
            options.inSampleSize=scaleSize;
            conn = (HttpURLConnection) myFileUrl.openConnection();
            //conn.setConnectTimeout(3000);
            conn.setDoInput(true);
            conn.connect();
            is = conn.getInputStream();
            bitmap = BitmapFactory.decodeStream(is,null,options);
            //对位图的宽度进行缩放
            try{
                int width = bitmap.getWidth();
                int height = bitmap.getHeight();
                Bitmap newBitmap;
//                Log.d("LOGCAT","scale:"+width+"-"+height+"-"+_w+"-"+_h);
                if(width>0 && height>0 && _w>0 && _h>0) {
                    float scaleWidth = ((float) bitmap.getHeight() * _w / _h) / bitmap.getWidth();
                    Matrix matrix = new Matrix();
                    matrix.postScale(scaleWidth, 1);
                    newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
                    is.close();
                    if(imagekeyList.size()>cacheMaxSize) {
                        imgCache.remove(imagekeyList.get(0));
                        imagekeyList.remove(0);
                    }
                    imagekeyList.add(url);
                    imgCache.put(url, bitmap);
                    return newBitmap;
                }
            }catch (Exception e){
            }
            is.close();
            if(imagekeyList.size()>cacheMaxSize) {
                imgCache.remove(imagekeyList.get(0));
                imagekeyList.remove(0);
            }
            imagekeyList.add(url);
            imgCache.put(url, bitmap);
            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //计算缩放量
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

    /**
     * 清除图片cache
     */
    public static void clearCache(){
        imgCache.clear();
    }
}

相关github地址:https://github.com/codeqian/android-class-lib

你可能感兴趣的:(Android基础控件之图片加载(带缓存))