Android图片的压缩处理和缓存策略

来源:互联网 发布:java文档管理系统 开源 编辑:程序博客网 时间:2024/06/06 15:54

我们平时工作中经常会处理Bitmap图片,有些网上下载下来的图片很大,比如1024*1024分辨率的图片,采用ARGB8888格式存储,每个像素占4个字节,就是1024*1024*4=4M,一张图片就这么大了,如果我们不进行压缩处理的话,很容易引起OOM,所以我们经常要对下载下来的图片进行压缩处理,如果每次都让用户重新去网上请求数据,这样显然是不合理的,对于已经下载下来的图片,当然要做缓存,我们可以做内存缓冲,再加一次存储设备的缓存,这样用户下次加载这个图片的时候,先去内存中读取,如果内存中已经没有了,再去存储设备上读取,如果存储设备上的缓存也没了,再去网上重新下载,这样节省了用户流量,提升了速度,也提升了用户体验。
BitmapFactory提供了decodeFile,decodeFileDescriptor,decodeResource,decodeStream,decodeByteArray等方法,分别用于从文件,资源,输入流以及字节数组中加载出一个Bitmap对象,BitmapFactory.Options里有一个很重要的成员变量inSampleSize,它就是用来缩放图片的,当inSampleSize为1时,图片大小不变,当inSampleSize为2时,缩放后的图片宽和高都为原来的1/2,那么整体像素数就是原来的1/4,图片大小也为原来的1/4,当
inSampleSize为4时,图片大小为原来的1/16,依次类推,再说BitmapFactory.Options另外一个成员变量inJustDecodeBounds,当inJustDecodeBounds为true时,调用decodeResource相关方法时,比如:

BitmapFactory.decodeResource(res, resId, options);

并不会真的生成一个Bitmap对象,但是该资源的真实大小已经传到参数options里了,这是一个轻量级的操作,我们一般先这么做获取到数据源图片的真实大小,然后再根据我们想设定的大小,计算出inSampleSize,然后对图片进行压缩,举个例子:

public Bitmap decodeSampledBitmapFromResource(Resources res,                                                  int resId, int reqWidth, int reqHeight) {        // First decode with inJustDecodeBounds=true to check dimensions        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeResource(res, resId, options);        // Calculate inSampleSize        options.inSampleSize = calculateInSampleSize(options, reqWidth,                reqHeight);        // Decode bitmap with inSampleSize set        options.inJustDecodeBounds = false;        return BitmapFactory.decodeResource(res, resId, options);    }
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 heightRatio = Math.round((float) height / (float) reqHeight);            final int widthRatio = Math.round((float) width / (float) reqWidth);            // 选择宽和高中最大的比率作为inSampleSize的值,这样可以保证最终图片的宽和高            // 一定都会大于等于目标的宽和高。            inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio;        }        return inSampleSize;    }

先把options.inJustDecodeBounds设置为true,然后调用BitmapFactory.decodeResource(res, resId, options),这样真实数据源的大小就会传到参数options里,然后调用calculateInSampleSize来计算inSampleSize,通过看源码我们可以知道取宽和高中最大的比率作为inSampleSize的值,这样可以保证最终图片的宽和高,然后再把options.inJustDecodeBounds设置为false,最后再次调用BitmapFactory.decodeResource(res, resId, options),生成Bitmap,通过以上步骤,就可以得到我们压缩后的图片了,最开始的函数Bitmap decodeSampledBitmapFromResource(Resources res,int resId, int reqWidth, int reqHeight)的最后两个参数,是用户希望缩放到的大小,应该很清晰了吧,下面看几个其他decode的例子:

public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {        // First decode with inJustDecodeBounds=true to check dimensions        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeFileDescriptor(fd, null, options);        // Calculate inSampleSize        options.inSampleSize = calculateInSampleSize(options, reqWidth,                reqHeight);        // Decode bitmap with inSampleSize set        options.inJustDecodeBounds = false;        return BitmapFactory.decodeFileDescriptor(fd, null, options);    }

看源码decodeFileDescriptor直接调用了底层的natvie方法,效率会比decodeFile高一些

private Bitmap decodeSampledBitmapFromFile(String pathName,            int reqWidth, int reqHeight) {        // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeFile(pathName, options);        options.inSampleSize = calculateInSampleSize(options, reqWidth,                reqHeight);        // 使用获取到的inSampleSize值再次解析图片        options.inJustDecodeBounds = false;        return BitmapFactory.decodeFile(pathName, options);    }

下面看下decodeStream:

    private Bitmap downloadBitmapFromUrl(String urlString) {        Bitmap bitmap = null;        HttpURLConnection urlConnection = null;        BufferedInputStream in = null;        try {            final URL url = new URL(urlString);            urlConnection = (HttpURLConnection) url.openConnection();            in = new BufferedInputStream(urlConnection.getInputStream(),                    IO_BUFFER_SIZE);            bitmap = BitmapFactory.decodeStream(in);        } catch (final IOException e) {            Log.e(TAG, "Error in downloadBitmap: " + e);        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }            MyUtils.close(in);        }        return bitmap;    }

通过上面的各个例子代码,我们发现其实都是差不多,图片压缩就讲到这里,下面来谈谈图片缓存:
内存级别的缓存我们用到LruCache类,存储设备的缓存我们用到DiskLruCache,前者目前已经在android源码里有了,后者需要去网上下载,但是据说是Goole推荐的方法了。
我们先看下LruCache怎么用:

private LruCache<String, Bitmap> mMemoryCache;int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);int cacheSize = maxMemory / 8;    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap bitmap) {                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;            }        };    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {        if (getBitmapFromMemCache(key) == null) {            mMemoryCache.put(key, bitmap);        }    }    private Bitmap getBitmapFromMemCache(String key) {        return mMemoryCache.get(key);    }

cacheSize设置为当前应用可分配最大内存的1/8,sizeOf返回的是单个图片的大小,通过mMemoryCache.put(key, bitmap);往缓存中添加一个bitmap,通过mMemoryCache.get(key)从缓存中得到一个bitmap,LruCache缓存策略在缓存空间不够的时候会把最近最少使用的图片清楚掉,查看源码发现,LruCache里有一个LinkedHashMap

public final V put(K key, V value) {        if (key == null || value == null) {            throw new NullPointerException("key == null || value == null");        }        V previous;        synchronized (this) {            putCount++;            size += safeSizeOf(key, value);            previous = map.put(key, value);            if (previous != null) {                size -= safeSizeOf(key, previous);            }        }        if (previous != null) {            entryRemoved(false, key, previous, value);        }        trimToSize(maxSize);        return previous;    }

调用了trimToSize方法:

public void trimToSize(int maxSize) {        while (true) {            K key;            V value;            synchronized (this) {                if (size < 0 || (map.isEmpty() && size != 0)) {                    throw new IllegalStateException(getClass().getName()                            + ".sizeOf() is reporting inconsistent results!");                }                if (size <= maxSize || map.isEmpty()) {                    break;                }                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();                key = toEvict.getKey();                value = toEvict.getValue();                map.remove(key);                size -= safeSizeOf(key, value);                evictionCount++;            }            entryRemoved(true, key, value, null);        }    }

里面是一个while(true)的循环,除非当前大小小于最大值,或者map是空,否则就会把链表末尾的数据给清楚掉,LruCache就说到这里,下面来看DiskLruCache:

private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;File diskCacheDir = getDiskCacheDir(mContext, "bitmap");        if (!diskCacheDir.exists()) {            diskCacheDir.mkdirs();        }        if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {            try {                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1,                        DISK_CACHE_SIZE);                mIsDiskLruCacheCreated = true;            } catch (IOException e) {                e.printStackTrace();            }        }
public File getDiskCacheDir(Context context, String uniqueName) {        boolean externalStorageAvailable = Environment                .getExternalStorageState().equals(Environment.MEDIA_MOUNTED);        final String cachePath;        if (externalStorageAvailable) {            cachePath = context.getExternalCacheDir().getPath();        } else {            cachePath = context.getCacheDir().getPath();        }        return new File(cachePath + File.separator + uniqueName);    }    @TargetApi(VERSION_CODES.GINGERBREAD)    private long getUsableSpace(File path) {        if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {            return path.getUsableSpace();        }        final StatFs stats = new StatFs(path.getPath());        return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();    }

getDiskCacheDir创建一个存储缓存的路径,getUsableSpace获取当前路径的可用空间,如果大于DISK_CACHE_SIZE = 1024 * 1024 * 50 //50M的话,就创建DiskLruCache这个对象,下面我们看看怎么从缓存中读取和怎么写入缓存:

String key = hashKeyFormUrl(url);        DiskLruCache.Editor editor = mDiskLruCache.edit(key);        if (editor != null) {            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);            if (downloadUrlToStream(url, outputStream)) {                editor.commit();            } else {                editor.abort();            }            mDiskLruCache.flush();        }
public boolean downloadUrlToStream(String urlString,                                       OutputStream outputStream) {        HttpURLConnection urlConnection = null;        BufferedOutputStream out = null;        BufferedInputStream in = null;        try {            final URL url = new URL(urlString);            urlConnection = (HttpURLConnection) url.openConnection();            in = new BufferedInputStream(urlConnection.getInputStream(),                    IO_BUFFER_SIZE);            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);            int b;            while ((b = in.read()) != -1) {                out.write(b);            }            return true;        } catch (IOException e) {            Log.e(TAG, "downloadBitmap failed." + e);        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }            try {                if (out != null) {                    out.close();                }                if (in != null) {                    in.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }        return false;    }
private String hashKeyFormUrl(String url) {        String cacheKey;        try {            final MessageDigest mDigest = MessageDigest.getInstance("MD5");            mDigest.update(url.getBytes());            cacheKey = bytesToHexString(mDigest.digest());        } catch (NoSuchAlgorithmException e) {            cacheKey = String.valueOf(url.hashCode());        }        return cacheKey;    }    private String bytesToHexString(byte[] bytes) {        StringBuilder sb = new StringBuilder();        for (int i = 0; i < bytes.length; i++) {            String hex = Integer.toHexString(0xFF & bytes[i]);            if (hex.length() == 1) {                sb.append('0');            }            sb.append(hex);        }        return sb.toString();    }

这里取url的md5值作为key,DiskLruCache.Editor editor = mDiskLruCache.edit(key);通过edit方法获取到一个DiskLruCache.Editor,OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);拿到outputStream ,只要往outputStream 写入数据,就是存到了缓存上,接下来看看怎么写入:

private Bitmap loadBitmapFromDiskCache(String url, int reqWidth,                                           int reqHeight) throws IOException {        if (Looper.myLooper() == Looper.getMainLooper()) {            Log.w(TAG, "load bitmap from UI Thread, it's not recommended!");        }        if (mDiskLruCache == null) {            return null;        }        Bitmap bitmap = null;        String key = hashKeyFormUrl(url);        DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);        if (snapShot != null) {            FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);            FileDescriptor fileDescriptor = fileInputStream.getFD();            bitmap = decodeSampledBitmapFromFileDescriptor(fileDescriptor,                    reqWidth, reqHeight);            if (bitmap != null) {                addBitmapToMemoryCache(key, bitmap);            }        }        return bitmap;    }

通过mDiskLruCache.get(key)获取到一个DiskLruCache.Snapshot,通过FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);获取到FileInputStream ,然后就可以读了,通过源码我们看到DiskLruCache跟LruCache一样,也有一个LinkedHashMap,那么也是最近最少使用算法了,由于是存到文件系统中,DiskLruCache维护了一个journal日志文件,里面会记录了每次存取的key值,每次用的时候去这个日志文件中读key值,然后把key值赋给LinkedHashMap,接下来就跟LruCache类似了,好了DiskLruCache就介绍到这里,下面我贴出一个完整的代码,是一个自己写的ImageLoader,用了LruCache缓存和DiskLruCache缓存,摘杂android开发艺术探索,先看核心代码:

public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {        Bitmap bitmap = loadBitmapFromMemCache(uri);        if (bitmap != null) {            Log.d(TAG, "loadBitmapFromMemCache,url:" + uri);            return bitmap;        }        try {            bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);            if (bitmap != null) {                Log.d(TAG, "loadBitmapFromDisk,url:" + uri);                return bitmap;            }            bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);            Log.d(TAG, "loadBitmapFromHttp,url:" + uri);        } catch (IOException e) {            e.printStackTrace();        }        if (bitmap == null && !mIsDiskLruCacheCreated) {            Log.w(TAG, "encounter error, DiskLruCache is not created.");            bitmap = downloadBitmapFromUrl(uri);        }        return bitmap;    }

先从内存缓存中读取,如果没有再去文件缓存中读取,还没有就下载到文件缓存再添加到内存缓存中,如果没有设置文件缓存,就直接从网上下载,下面是全部代码:

package com.fq.myimageloader;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileDescriptor;import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.concurrent.Executor;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadFactory;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;import android.annotation.TargetApi;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Build;import android.os.Build.VERSION_CODES;import android.os.Environment;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.os.StatFs;import android.support.v4.util.LruCache;import android.util.Log;import android.widget.ImageView;public class MyImageLoader {    private static final String TAG = "ImageLoader";    public static final int MESSAGE_POST_RESULT = 1;    private static final int CPU_COUNT = Runtime.getRuntime()            .availableProcessors();    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;    private static final long KEEP_ALIVE = 10L;    private static final int TAG_KEY_URI = R.id.imageloader_uri;    private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;    private static final int IO_BUFFER_SIZE = 8 * 1024;    private static final int DISK_CACHE_INDEX = 0;    private boolean mIsDiskLruCacheCreated = false;    private static final ThreadFactory sThreadFactory = new ThreadFactory() {        private final AtomicInteger mCount = new AtomicInteger(1);        public Thread newThread(Runnable r) {            return new Thread(r, "ImageLoader#" + mCount.getAndIncrement());        }    };    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,            KEEP_ALIVE, TimeUnit.SECONDS,            new LinkedBlockingQueue<Runnable>(), sThreadFactory);    private Handler mMainHandler = new Handler(Looper.getMainLooper()) {        @Override        public void handleMessage(Message msg) {            LoaderResult result = (LoaderResult) msg.obj;            ImageView imageView = result.imageView;            imageView.setImageBitmap(result.bitmap);            String uri = (String) imageView.getTag(TAG_KEY_URI);            if (uri.equals(result.uri)) {                imageView.setImageBitmap(result.bitmap);            } else {                Log.w(TAG, "set image bitmap,but url has changed, ignored!");            }        }    };    private Context mContext;    private LruCache<String, Bitmap> mMemoryCache;    private DiskLruCache mDiskLruCache;    public MyImageLoader(Context context) {        mContext = context.getApplicationContext();        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);        int cacheSize = maxMemory / 8;        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap bitmap) {                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;            }        };        File diskCacheDir = getDiskCacheDir(mContext, "bitmap");        if (!diskCacheDir.exists()) {            diskCacheDir.mkdirs();        }        if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {            try {                mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1,                        DISK_CACHE_SIZE);                mIsDiskLruCacheCreated = true;            } catch (IOException e) {                e.printStackTrace();            }        }    }    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {        if (getBitmapFromMemCache(key) == null) {            mMemoryCache.put(key, bitmap);        }    }    private Bitmap getBitmapFromMemCache(String key) {        return mMemoryCache.get(key);    }    /**     * load bitmap from memory cache or disk cache or network async, then bind imageView and bitmap.     * NOTE THAT: should run in UI Thread     * @param uri http url     * @param imageView bitmap's bind object     */    public void bindBitmap(final String uri, final ImageView imageView) {        bindBitmap(uri, imageView, 0, 0);    }    public void bindBitmap(final String uri, final ImageView imageView,                           final int reqWidth, final int reqHeight) {        imageView.setTag(TAG_KEY_URI, uri);        Bitmap bitmap = loadBitmapFromMemCache(uri);        if (bitmap != null) {            imageView.setImageBitmap(bitmap);            return;        }        Runnable loadBitmapTask = new Runnable() {            @Override            public void run() {                Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);                if (bitmap != null) {                    LoaderResult result = new LoaderResult(imageView, uri, bitmap);                    mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result).sendToTarget();                }            }        };        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);    }    /**     * load bitmap from memory cache or disk cache or network.     * @param uri http url     * @param reqWidth the width ImageView desired     * @param reqHeight the height ImageView desired     * @return bitmap, maybe null.     */    public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {        Bitmap bitmap = loadBitmapFromMemCache(uri);        if (bitmap != null) {            Log.d(TAG, "loadBitmapFromMemCache,url:" + uri);            return bitmap;        }        try {            bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);            if (bitmap != null) {                Log.d(TAG, "loadBitmapFromDisk,url:" + uri);                return bitmap;            }            bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);            Log.d(TAG, "loadBitmapFromHttp,url:" + uri);        } catch (IOException e) {            e.printStackTrace();        }        if (bitmap == null && !mIsDiskLruCacheCreated) {            Log.w(TAG, "encounter error, DiskLruCache is not created.");            bitmap = downloadBitmapFromUrl(uri);        }        return bitmap;    }    private Bitmap loadBitmapFromMemCache(String url) {        final String key = hashKeyFormUrl(url);        Bitmap bitmap = getBitmapFromMemCache(key);        return bitmap;    }    private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight)            throws IOException {        if (Looper.myLooper() == Looper.getMainLooper()) {            throw new RuntimeException("can not visit network from UI Thread.");        }        if (mDiskLruCache == null) {            return null;        }        String key = hashKeyFormUrl(url);        DiskLruCache.Editor editor = mDiskLruCache.edit(key);        if (editor != null) {            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);            if (downloadUrlToStream(url, outputStream)) {                editor.commit();            } else {                editor.abort();            }            mDiskLruCache.flush();        }        return loadBitmapFromDiskCache(url, reqWidth, reqHeight);    }    private Bitmap loadBitmapFromDiskCache(String url, int reqWidth,                                           int reqHeight) throws IOException {        if (Looper.myLooper() == Looper.getMainLooper()) {            Log.w(TAG, "load bitmap from UI Thread, it's not recommended!");        }        if (mDiskLruCache == null) {            return null;        }        Bitmap bitmap = null;        String key = hashKeyFormUrl(url);        DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);        if (snapShot != null) {            FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);            FileDescriptor fileDescriptor = fileInputStream.getFD();            bitmap = decodeSampledBitmapFromFileDescriptor(fileDescriptor,                    reqWidth, reqHeight);            if (bitmap != null) {                addBitmapToMemoryCache(key, bitmap);            }        }        return bitmap;    }    public boolean downloadUrlToStream(String urlString,                                       OutputStream outputStream) {        HttpURLConnection urlConnection = null;        BufferedOutputStream out = null;        BufferedInputStream in = null;        try {            final URL url = new URL(urlString);            urlConnection = (HttpURLConnection) url.openConnection();            in = new BufferedInputStream(urlConnection.getInputStream(),                    IO_BUFFER_SIZE);            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);            int b;            while ((b = in.read()) != -1) {                out.write(b);            }            return true;        } catch (IOException e) {            Log.e(TAG, "downloadBitmap failed." + e);        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }            try {                if (out != null) {                    out.close();                }                if (in != null) {                    in.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }        return false;    }    private Bitmap downloadBitmapFromUrl(String urlString) {        Bitmap bitmap = null;        HttpURLConnection urlConnection = null;        BufferedInputStream in = null;        try {            final URL url = new URL(urlString);            urlConnection = (HttpURLConnection) url.openConnection();            in = new BufferedInputStream(urlConnection.getInputStream(),                    IO_BUFFER_SIZE);            bitmap = BitmapFactory.decodeStream(in);        } catch (final IOException e) {            Log.e(TAG, "Error in downloadBitmap: " + e);        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }            try {                if (in != null) {                    in.close();                }            }catch (Exception e){                e.printStackTrace();            }        }        return bitmap;    }    private String hashKeyFormUrl(String url) {        String cacheKey;        try {            final MessageDigest mDigest = MessageDigest.getInstance("MD5");            mDigest.update(url.getBytes());            cacheKey = bytesToHexString(mDigest.digest());        } catch (NoSuchAlgorithmException e) {            cacheKey = String.valueOf(url.hashCode());        }        return cacheKey;    }    private String bytesToHexString(byte[] bytes) {        StringBuilder sb = new StringBuilder();        for (int i = 0; i < bytes.length; i++) {            String hex = Integer.toHexString(0xFF & bytes[i]);            if (hex.length() == 1) {                sb.append('0');            }            sb.append(hex);        }        return sb.toString();    }    public File getDiskCacheDir(Context context, String uniqueName) {        boolean externalStorageAvailable = Environment                .getExternalStorageState().equals(Environment.MEDIA_MOUNTED);        final String cachePath;        if (externalStorageAvailable) {            cachePath = context.getExternalCacheDir().getPath();        } else {            cachePath = context.getCacheDir().getPath();        }        return new File(cachePath + File.separator + uniqueName);    }    @TargetApi(VERSION_CODES.GINGERBREAD)    private long getUsableSpace(File path) {        if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {            return path.getUsableSpace();        }        final StatFs stats = new StatFs(path.getPath());        return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();    }    private static class LoaderResult {        public ImageView imageView;        public String uri;        public Bitmap bitmap;        public LoaderResult(ImageView imageView, String uri, Bitmap bitmap) {            this.imageView = imageView;            this.uri = uri;            this.bitmap = bitmap;        }    }    public Bitmap decodeSampledBitmapFromResource(Resources res,                                                  int resId, int reqWidth, int reqHeight) {        // First decode with inJustDecodeBounds=true to check dimensions        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeResource(res, resId, options);        // Calculate inSampleSize        options.inSampleSize = calculateInSampleSize(options, reqWidth,                reqHeight);        // Decode bitmap with inSampleSize set        options.inJustDecodeBounds = false;        return BitmapFactory.decodeResource(res, resId, options);    }    public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {        // First decode with inJustDecodeBounds=true to check dimensions        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeFileDescriptor(fd, null, options);        // Calculate inSampleSize        options.inSampleSize = calculateInSampleSize(options, reqWidth,                reqHeight);        // Decode bitmap with inSampleSize set        options.inJustDecodeBounds = false;        return BitmapFactory.decodeFileDescriptor(fd, null, options);    }    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 heightRatio = Math.round((float) height / (float) reqHeight);            final int widthRatio = Math.round((float) width / (float) reqWidth);            // 选择宽和高中最大的比率作为inSampleSize的值,这样可以保证最终图片的宽和高            // 一定都会大于等于目标的宽和高。            inSampleSize = heightRatio < widthRatio ? widthRatio : heightRatio;        }        return inSampleSize;    }}

代码摘自android开发艺术探索一书。
好了,Android图片压缩处理和缓存策略就分析到这里,如有问题,欢迎指正,谢谢。

0 0