Android中的图片三级缓存

Android中的图片三级缓存


为什么要使用三级缓存
  • 如今时代,获取网络图片是件再正常不过的事情。
  • 假如每次启动应用程序都要重新从网络获取图片,流量使用量将是巨大的。
  • 所以提出图片三级缓存。内存,本地,网络三级缓存来减少和网络之间不必要的交互,避免流量浪费。
什么是三级缓存
  • 内存缓存 优先加载。
  • 本地缓存 次优先加载。
  • 网络缓存 后加载,速度慢。
三级缓存原理
  • 首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
  • 之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
  • 总之,只在初次访问新内容时,才通过网络获取图片资源
具体实现代码

1 . 自定义图片缓存工具类(CacheUtils)

import android.graphics.Bitmap;
import android.util.Log;
import android.widget.ImageView;

public class CacheUtils {
    private static final String TAG = "CacheUtils";
    private MemoryCacheUtils mMemoryCacheUtils;
    private LocalCacheUtils mLocalCacheUtils;
    private NetCacheUtils mNetCacheUtils;

    public CacheUtils() {
        mMemoryCacheUtils = new MemoryCacheUtils();
        mLocalCacheUtils = new LocalCacheUtils();
        mNetCacheUtils = new NetCacheUtils(mMemoryCacheUtils, mLocalCacheUtils);
    }

    public void diaplay(ImageView imageView, String url) {

        //内存缓存   生命周期同调用者
        Bitmap bitmap = mMemoryCacheUtils.getBitmapToMemory(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            Log.i(TAG, "从内存中获取图片******");
            return;
        }


        //本地缓存
        bitmap = LocalCacheUtils.getBitmapToLoacl(url);
        if (bitmap != null) {
            Log.i(TAG, "从本地中获取图片******");
            imageView.setImageBitmap(bitmap);
            mMemoryCacheUtils.putBitmapToMemory(bitmap, url);
            return;
        }

        //网络缓存
        mNetCacheUtils.getBitmapFromNet(imageView, url);
    }
}

2 . 网络缓存

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class NetCacheUtils {
    private MemoryCacheUtils mMemoryCacheUtils;
    private LocalCacheUtils mLocalCacheUtils;

    public NetCacheUtils(MemoryCacheUtils mMemoryCacheUtils, LocalCacheUtils mLocalCacheUtils) {
        this.mLocalCacheUtils = mLocalCacheUtils;
        this.mMemoryCacheUtils = mMemoryCacheUtils;
    }

        /**
         * 从网络下载图片
         * @param imageView 显示图片的imageview
         * @param url   下载图片的网络地址
         */
    public void getBitmapFromNet(ImageView imageView, String url) {
        imageView.setTag(url);
        Bitmaptask task = new Bitmaptask();
        task.execute(imageView, url);
    }

        /**
         * AsyncTask就是对handler和线程池的封装
         * 第一个泛型:参数类型
         * 第二个泛型:更新进度的泛型
         * 第三个泛型:onPostExecute的返回结果
         */
    class Bitmaptask extends AsyncTask<Object, Void, Bitmap> {

        private HttpURLConnection urlConnection;
        private ImageView imageView;
        private String url;

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null) {
                if (url.equals(imageView.getTag())) {
                    imageView.setImageBitmap(bitmap);
                    System.out.print("onPostExecute"+"从网络获取图片*****");
                    mLocalCacheUtils.putBitmapToLoacl(bitmap, url);
                    mMemoryCacheUtils.putBitmapToMemory(bitmap, url);
                }
            }
        }

        @Override
        protected Bitmap doInBackground(Object[] params) {
            imageView = (ImageView) params[0];
            url = (String) params[1];
            Bitmap bitmap = downloadFromNet(url);
            return bitmap;
        }

        private Bitmap downloadFromNet(String url) {
            try {
                urlConnection = (HttpURLConnection) new URL(url).openConnection();
                urlConnection.setConnectTimeout(5000);
                urlConnection.setRequestMethod("GET");
                int responseCode = urlConnection.getResponseCode();
                if (responseCode == 200) {
                    InputStream inputStream = urlConnection.getInputStream();
                    BitmapFactory.Options opts = new BitmapFactory.Options();
                    opts.inSampleSize = 2;//压缩图片2倍
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, opts);
                    return bitmap;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            } finally {
                urlConnection.disconnect();
            }
            return null;
        }
    }
}

3 . 本地缓存

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

import static android.content.ContentValues.TAG;

public class LocalCacheUtils {

        /**
         * 从网络获取图片后,保存至本地缓存
         * @param url
         * @param bitmap
         */
    public static void putBitmapToLoacl(Bitmap bitmap, String url) {
        String encode = Md5Utils.encode(url);//使用路径为文件名,进行md5加密
        File file = new File(Environment.getExternalStorageDirectory(), encode);
        Log.i(TAG, "putBitmapToLoacl: " + file.toString());
        File parent = file.getParentFile();
        if (!parent.exists()) {
            parent.mkdirs();
        }
        try {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

        /**
         * 从本地读取图片
         * @param url
         */
    public static Bitmap getBitmapToLoacl(String url) {
        String encode = Md5Utils.encode(url);
        File file = new File(Environment.getExternalStorageDirectory(), encode);
        if (file.exists()) {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inSampleSize = 3;
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file), null, opts);
                return bitmap;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }
}

4 . 内存缓存

通过 HashMap<String,Bitmap>键值对的方式保存图片,key为地址,value为图片对象,但因是强引用对象,很容易造成内存溢出,可以尝试SoftReference软引用对象。

通过 HashMap<String, SoftReference<Bitmap>>SoftReference 为软引用对象(GC垃圾回收会自动回收软引用对象),但在Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache

通过 LruCache<String,Bitmap> least recentlly use 最少最近使用算法
会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定.

import android.graphics.Bitmap;
import android.util.Log;
import android.util.LruCache;

import static android.content.ContentValues.TAG;

public class MemoryCacheUtils {

    private LruCache<String, Bitmap> mMemoryCache;

    public MemoryCacheUtils() {
        int maxmemory = (int) Runtime.getRuntime().maxMemory();
        Log.i(TAG, "MemoryCacheUtils: " + maxmemory);
        mMemoryCache = new LruCache<String, Bitmap>(maxmemory / 8) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }
        };
    }

        /**
         * 往内存中写图片
         * @param url
         * @param bitmap
         */
    public void putBitmapToMemory(Bitmap bitmap, String url) {
        Log.i(TAG, "putBitmapToMemory: ");
        mMemoryCache.put(url, bitmap);
    }

        /**
         * 从内存中读图片
         * @param url
         */
    public Bitmap getBitmapToMemory(String url) {
        Log.i(TAG, "getBitmapToMemory: ");
        Bitmap bitmap = mMemoryCache.get(url);
        return bitmap;
    }
}

5 . 本地缓存用到的md5工具类

import java.security.MessageDigest;

public class Md5Utils {
    public static String encode(String pwd) {
        try {
            MessageDigest digest = MessageDigest.getInstance("md5");
            byte[] bs = digest.digest(pwd.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : bs) {
                int number = b & 0xff;
                String str = Integer.toHexString(number);
                if (str.length() == 1) {
                    sb.append("0");
                }
                sb.append(number);
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值