加载Bitmap(ImageLoader)

添加DiskLruCache的依赖:

compile 'com.jakewharton:disklrucache:2.0.2'

图片压缩

package com.example.bitmap_demo;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 * 

文件描述:

*

创建时间:2019/3/10

*/ public class ImageResizer { private static final String TAG = "vera ImageResizer"; public Bitmap decodeSampledBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight){ // 可以通过BitmapFactory.Options来按照一定的采样率加载缩小后的图片 final BitmapFactory.Options options=new BitmapFactory.Options(); // 当inJustDecodeBounds设置为true时,BitmapFactory只会解析图片的原始大小,并不会真正加载图片 options.inJustDecodeBounds=true; BitmapFactory.decodeResource(res,resId,options); // 设置采样率 options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight); options.inJustDecodeBounds=false; return BitmapFactory.decodeResource(res,resId,options); } private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { if(reqWidth==0||reqHeight==0)return 1; final int rawHeight=options.outHeight; final int rawWidth=options.outWidth; Log.e(TAG, "calculateInSampleSize: origin width:"+rawWidth+"origin height:"+rawHeight ); int inSampleSize=1; if(rawHeight>reqHeight||rawWidth>reqWidth){ final int halfWidth=rawWidth/2; final int halfHeight=rawHeight/2; // 如果改为||的话会导致所缩放后的图片小于ImageView所期望的大小,这样图片会被拉伸导致图片模糊。 while((halfHeight/inSampleSize)>=reqHeight&& (halfWidth/inSampleSize)>reqWidth){ inSampleSize*=2; } } // 如果图片原始大小本来就小于ImageView期望的大小,返回1. Log.e(TAG, "calculateInSampleSize: "+inSampleSize ); return inSampleSize; } public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd,int reqWidth,int reqHeight){ final BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFileDescriptor(fd,null,options); options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight); options.inJustDecodeBounds=false; return BitmapFactory.decodeFileDescriptor(fd,null,options); } }

在ImageLoader的构造函数中初始化LruCache和DiskLruCache:

package com.example.bitmap_demo;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.util.LruCache;

import com.jakewharton.disklrucache.DiskLruCache;

import java.io.File;
import java.io.IOException;

/**
 * 

文件描述:

*

创建时间:2019/3/10

*/ public class ImageLoader { private boolean mIsDiskLruCacheCreated=false; private Context mContext; private LruCache<String , Bitmap> mLruCache;//内存缓存 private DiskLruCache mDiskLruCache;//磁盘缓存 private static final int DISK_CACHE_SIZE = 50*1024*1024;//50M public ImageLoader(Context context) { mContext = context; int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024);//单位是kb,当前进程可用内存 int cacheSize=maxMemory/8; mLruCache=new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight()/1024;//单位是kb,返回Bitmap对象的大小 } }; File diskCacheDir=getDiskCacheDir(mContext,"bitmap"); //在sd card中的位置 if(!diskCacheDir.exists()){ diskCacheDir.mkdirs(); } // 如果磁盘剩余空间小于DISK_CACHE_SIZE无法创建DiskLruCache if(getUsableSpace(diskCacheDir)>DISK_CACHE_SIZE){ try { mDiskLruCache=DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE); } catch (IOException e) { e.printStackTrace(); } mIsDiskLruCacheCreated=true; } } private long getUsableSpace(File diskCacheDir) { if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.GINGERBREAD){ return diskCacheDir.getUsableSpace(); } final StatFs statFs=new StatFs(diskCacheDir.getPath()); return statFs.getBlockSize()*statFs.getAvailableBlocks(); } private File getDiskCacheDir(Context context, String Name) { 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+Name); } }

内存缓存的添加和获取:

    private void addBitmapToMemoryCache(String key,Bitmap bitmap){
        if(getBitmapFromMemoryCache(key)==null){
            mLruCache.put(key,bitmap);
        }
    }
    private Bitmap getBitmapFromMemoryCache(String key){
        return mLruCache.get(key);
    }

磁盘缓存的添加和获取:

    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.e(TAG, "loadBitmapFromDiskCache: 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 fileDescripto=fileInputStream.getFD();
            bitmap=mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescripto,reqWidth,reqHeight);
            if (bitmap!=null)addBitmapToMemoryCache(key,bitmap);
        }
        return bitmap;
    }

    //有了文件输出流,图片就可以通过这个文件输出流写入到文件系统上
    private 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) {
            e.printStackTrace();
        }finally {
            if(urlConnection!=null)urlConnection.disconnect();
            try {
                out.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return false;

    }

    //图片的url可能会有特殊字符
    private String hashKeyFormUrl(String url) {
        String cacheKey;
        try {
            final MessageDigest messageDigest=MessageDigest.getInstance("MD5");
            messageDigest.update(url.getBytes());
            cacheKey=bytesToHexString(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey=String.valueOf(url.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder stringBuilder=new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex=Integer.toHexString(0xFF&bytes[i]);
            if(hex.length()==1)stringBuilder.append('0');
            stringBuilder.append(hex);
        }
        return stringBuilder.toString();
    }

同步加载和异步加载接口的设计:

    /**
     * 同步加载接口需要外部在线程中调用,因为同步加载可能很耗时
     */
    public Bitmap loadBitmap(String uri,int reqWidth,int reqHeight){
        Bitmap bitmap=loadBitmapFromMemCache(uri);//首先尝试从内存获取图片
        if (bitmap!=null){
            Log.e(TAG, "loadBitmap:loadBitmapFromMemCache,url: "+uri );
            return bitmap;
        }
        try {
            bitmap=loadBitmapFromDiskCache(uri,reqWidth,reqHeight);//再尝试从磁盘获取
            if (bitmap!=null){
                Log.e(TAG, "loadBitmap:loadBitmapFromDiskCache,url: "+uri );
                return bitmap;
            }
            bitmap=loadBitmapFromHttp(uri,reqWidth,reqHeight);//再尝试从网络拉取
            Log.e(TAG, "loadBitmap: loadBitmapFromHttp"+uri );
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (bitmap==null&&!mIsDiskLruCacheCreated){
            Log.e(TAG, "loadBitmap: encounter error,DiskLruCache is not created." );
            bitmap=downloadBitmapFromUrl(uri);
        }
        return bitmap;
    }

    /**
     * 异步加载
     */
    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;
        }
//        如果没用从内存中获取到图片,就去线程池中调用loadBitmap()方法。
        Runnable loadBitmapTask=new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap1=loadBitmap(uri,reqWidth,reqHeight);
                if (bitmap1!=null){
//                    图片加载成功之后,封装成一个LoadResult对象
                    LoaderResult result=new LoaderResult(imageView,uri,bitmap1);
                    //                    通过Handler向主线程发送消息,在主线程中给imageVIew设置照片
                    mMainHandler.obtainMessage(MESSAGE_POST_RESULT,result).sendToTarget();
                }
            }
        };
        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }

完整的ImageLoader:

package com.example.bitmap_demo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StatFs;
import android.support.annotation.NonNull;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;

import com.jakewharton.disklrucache.DiskLruCache;

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.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;

/**
 * 

文件描述:

*

创建时间:2019/3/10

*/ public class ImageLoader { private static final String TAG = "vera"; private static final int IO_BUFFER_SIZE = 8*1024;//8kb private static final int TAG_KEY_URI =R.id.imageloader_uri; private static final int MESSAGE_POST_RESULT = 1; private boolean mIsDiskLruCacheCreated=false; private Context mContext; private LruCache<String , Bitmap> mLruCache;//内存缓存 private DiskLruCache mDiskLruCache;//磁盘缓存 private ImageResizer mImageResizer=new ImageResizer(); private static final int DISK_CACHE_SIZE = 50*1024*1024;//50M private int DISK_CACHE_INDEX; private static final int CPU_COUNT=Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE=CPU_COUNT+1; private static final int MAX_POOL_SIZE=CPU_COUNT*2+1; private static final long KEEP_ALIVE=10L; private static final ThreadFactory sThreadFactory=new ThreadFactory() { private final AtomicInteger mCount=new AtomicInteger(1); @Override public Thread newThread(@NonNull Runnable r) { return new Thread(r,"ImageLoader#"+mCount.getAndDecrement()); } }; public static final Executor THREAD_POOL_EXECUTOR=new ThreadPoolExecutor( CORE_POOL_SIZE,MAX_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(),sThreadFactory ); private Handler mMainHandler=new Handler(Looper.getMainLooper()){ @Override public void handleMessage(Message msg) { LoaderResult result= (LoaderResult) msg.obj; ImageView imageView=result.imageView; String uri= (String) imageView.getTag(TAG_KEY_URI); if (uri.equals(result.uri)){ imageView.setImageBitmap(result.bitmap); }else { Log.e(TAG, "handleMessage: set image bitmap,but url has changed,ignores!" ); } } }; public ImageLoader(Context context) { mContext = context; int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024);//单位是kb,当前进程可用内存 int cacheSize=maxMemory/8; mLruCache=new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight()/1024;//单位是kb,返回Bitmap对象的大小 } }; File diskCacheDir=getDiskCacheDir(mContext,"bitmap"); //在sd card中的位置 if(!diskCacheDir.exists()){ diskCacheDir.mkdirs(); } // 如果磁盘剩余空间小于DISK_CACHE_SIZE无法创建DiskLruCache if(getUsableSpace(diskCacheDir)>DISK_CACHE_SIZE){ try { mDiskLruCache=DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE); } catch (IOException e) { e.printStackTrace(); } mIsDiskLruCacheCreated=true; } } public static ImageLoader build(Context context){ return new ImageLoader(context); } private long getUsableSpace(File diskCacheDir) { if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.GINGERBREAD){ return diskCacheDir.getUsableSpace(); } final StatFs statFs=new StatFs(diskCacheDir.getPath()); return statFs.getBlockSize()*statFs.getAvailableBlocks(); } private File getDiskCacheDir(Context context, String Name) { 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+Name); } private void addBitmapToMemoryCache(String key,Bitmap bitmap){ if(getBitmapFromMemoryCache(key)==null){ mLruCache.put(key,bitmap); } } private Bitmap getBitmapFromMemoryCache(String key){ return mLruCache.get(key); } 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.e(TAG, "loadBitmapFromDiskCache: 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 fileDescripto=fileInputStream.getFD(); bitmap=mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescripto,reqWidth,reqHeight); if (bitmap!=null)addBitmapToMemoryCache(key,bitmap); } return bitmap; } //有了文件输出流,图片就可以通过这个文件输出流写入到文件系统上 private 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) { e.printStackTrace(); }finally { if(urlConnection!=null)urlConnection.disconnect(); try { out.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } return false; } //图片的url可能会有特殊字符 private String hashKeyFormUrl(String url) { String cacheKey; try { final MessageDigest messageDigest=MessageDigest.getInstance("MD5"); messageDigest.update(url.getBytes()); cacheKey=bytesToHexString(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey=String.valueOf(url.hashCode()); } return cacheKey; } private String bytesToHexString(byte[] bytes) { StringBuilder stringBuilder=new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex=Integer.toHexString(0xFF&bytes[i]); if(hex.length()==1)stringBuilder.append('0'); stringBuilder.append(hex); } return stringBuilder.toString(); } /** * 同步加载接口需要外部在线程中调用,因为同步加载可能很耗时 */ public Bitmap loadBitmap(String uri,int reqWidth,int reqHeight){ Bitmap bitmap=loadBitmapFromMemCache(uri);//首先尝试从内存获取图片 if (bitmap!=null){ Log.e(TAG, "loadBitmap:loadBitmapFromMemCache,url: "+uri ); return bitmap; } try { bitmap=loadBitmapFromDiskCache(uri,reqWidth,reqHeight);//再尝试从磁盘获取 if (bitmap!=null){ Log.e(TAG, "loadBitmap:loadBitmapFromDiskCache,url: "+uri ); return bitmap; } bitmap=loadBitmapFromHttp(uri,reqWidth,reqHeight);//再尝试从网络拉取 Log.e(TAG, "loadBitmap: loadBitmapFromHttp"+uri ); } catch (IOException e) { e.printStackTrace(); } if (bitmap==null&&!mIsDiskLruCacheCreated){ Log.e(TAG, "loadBitmap: encounter error,DiskLruCache is not created." ); bitmap=downloadBitmapFromUrl(uri); } return bitmap; } /** * 异步加载 */ 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; } // 如果没用从内存中获取到图片,就去线程池中调用loadBitmap()方法。 Runnable loadBitmapTask=new Runnable() { @Override public void run() { Bitmap bitmap1=loadBitmap(uri,reqWidth,reqHeight); if (bitmap1!=null){ // 图片加载成功之后,封装成一个LoadResult对象 LoaderResult result=new LoaderResult(imageView,uri,bitmap1); // 通过Handler向主线程发送消息,在主线程中给imageVIew设置照片 mMainHandler.obtainMessage(MESSAGE_POST_RESULT,result).sendToTarget(); } } }; THREAD_POOL_EXECUTOR.execute(loadBitmapTask); } 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 (IOException e) { Log.e(TAG, "downloadBitmapFromUrl: error " +e); }finally { if(urlConnection!=null)urlConnection.disconnect(); try { in.close(); } catch (IOException e) { e.printStackTrace(); } } return bitmap; } private Bitmap loadBitmapFromMemCache(String url) { final String key=hashKeyFormUrl(url); Bitmap bitmap=getBitmapFromMemoryCache(key); return bitmap; } 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; } } }

你可能感兴趣的:(安卓)