DiskCacheDir + LruCache + AsynTask 的封装,实现照片墙的效果

​ 上次对 DiskLruCache 进行了封装,但是他只能每次加载一张图片,不能放在ListView 等控件中使用。下面进行一次二次封装。

首先是网络请求

public class NetRequest {
    private OkHttpClient client = new OkHttpClient();
    public byte[] request(final String url) {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        try {
            final ResponseBody body = client.newCall(request).execute().body();
            if (body != null) {
                return body.bytes();
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }

然后是封装的 DiskLruCache

 * @author Lv
 * Created at 2019/6/14
 *
 * 图片缓存
 */
@SuppressWarnings({"ConstantConditions", "ResultOfMethodCallIgnored"})
public class DiskBitmapCache {

    private DiskLruCache mDiskLruCache;
    private Handler mHandler = new Handler(Looper.myLooper());

    public DiskBitmapCache(Context context, String uniqueName){
        open(context,uniqueName);
    }
    public DiskBitmapCache(){}

    /**
     * 用于返回 硬盘缓存后的数据
     */
    public interface OnCacheDataListener {
        /**
         * 返回数据
         *
         * @param bytes 缓存的数据
         */
        void onData(byte[] bytes);
    }

    /**
     * 打开磁盘缓存
     *
     * @param context    Context
     * @param uniqueName 缓存文件夹的名字
     * @return 返回一个 boolean 类型,true 表示 创建成果
     */
    public boolean open(Context context, String uniqueName) {
        File cacheDir = getCacheDir(context, uniqueName);
        //路径是否存在,不存在则创建
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }
        try {
            // 1,数据的缓存地址,2,指定当前应用程序的版本号
            // 3,指定同一个 key 可以对应多少个缓存文件,基本都是1
            // 4,指定据图可以缓存多少字节的数据
            mDiskLruCache = DiskLruCache.open(cacheDir, getVersion(context),
                    1, 10 * 1024 * 1024);
            return mDiskLruCache != null;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 传入对应的 url ,进行缓存
     *
     * @param url      需要缓存的图片 ,缓存的文件名字为 url
     * @param listener 回调,将数据进行缓存后,返回一份,如果不需要传入 null 即可。
     */
    public void writeData(String url, final OnCacheDataListener listener) {
        downloadData(url, listener);
    }

    /**
     * 传入指定的 key 和 bitmap ,对图片进行缓存
     *
     * @param key    这个key 为缓存文件的名字
     * @param bitmap 要缓存的图片
     * @return 返回缓存的结果
     */
    public boolean writeData(String key, Bitmap bitmap) {
        key = hashKeyForDisk(key);
        DiskLruCache.Editor edit = null;
        try {
            edit = mDiskLruCache.edit(key);
            if (edit != null) {
                OutputStream outputStream = edit.newOutputStream(0);
                boolean compress = bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                edit.commit();
                return compress;
            } else {
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            if (edit != null) {
                try {
                    edit.abort();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            return false;
        }
    }

    /**
     * 根据 传入的 key 来查找缓存
     *
     * @param key 用来读取缓存的 key
     * @return 返回缓存的图片字节,没有则为 null
     */
    public byte[] readCache(String key) {
        try {
            List<Byte> data = new ArrayList<>();
            key = hashKeyForDisk(key);
            DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
            if (snapShot != null) {
                InputStream is = snapShot.getInputStream(0);
                byte[] bytes = new byte[2048];
                int len;
                while ((len = is.read(bytes)) != -1) {
                    for (int i = 0; i < len; i++) {
                        data.add(bytes[i]);
                    }
                }
                bytes = new byte[data.size()];
                for (int i = 0; i < bytes.length; i++) {
                    bytes[i] = data.get(i);
                }
                return bytes;
            } else {
                return null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据 key 删除指定的 缓存
     *
     * @param key 缓存的 key
     * @return 成功则返回 true
     */
    public boolean removeCache(String key) {
        String md5Key = hashKeyForDisk(key);
        boolean remove = false;
        try {
            remove = mDiskLruCache.remove(md5Key);
            return remove;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return remove;
    }

    /**
     *  判断 该key 是否由缓存的图片
     * @param key 缓存文件对应的 key
     * @return 返回true 表示 有缓存,可以直接读取
     */
    public boolean isCache(String key) {
        byte[] bytes = readCache(key);
        return bytes != null;
    }

    /**
     * @return 返回 DiskLruCache 的实例
     */
    public DiskLruCache getInstance() {
        return mDiskLruCache;
    }

    /**
     * @return 返回缓存的大小,以字节为单位
     */
    public long size() {
        return mDiskLruCache.size();
    }

    /**
     * 将内存中的操作记录同步到日志文件,这个方法非常重要
     * 频繁地调用这个这个方法不会有任何好处,标准的做法是在 onPause 中调用一次就可以了
     */
    public void flush() {
        if (mDiskLruCache != null) {
            try {
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 这个方法用于将DiskLruCache关闭掉,是和open()方法对应的一个方法。
     * 关闭掉了之后就不能再调用DiskLruCache中任何操作缓存数据的方法,
     * 通常只应该在Activity的onDestroy()方法中去调用close()方法
     */
    public void close() {
        if (mDiskLruCache != null) {
            try {
                mDiskLruCache.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 这个方法用于将所有的缓存数据全部删除
     */
    public void delete() {
        try {
            mDiskLruCache.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @return 获取 缓存的路径
     */
    private File getCacheDir(Context context, String uniqueName) {
        String cachePath;
        // 判断 SD 卡是否可用
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            //获取 有sd 卡时的路径
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            // 获取 无sd 卡时的路径
            cachePath = context.getCacheDir().getPath();
        }
        //File.separator 分隔符 /
        return new File(cachePath + File.separator + uniqueName);
    }

    private int getVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }

    private String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] digest) {
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            String hex = Integer.toHexString(0xFF & b);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }
    private void downloadData(final String url, final OnCacheDataListener listener) {
        try {
            String key = hashKeyForDisk(url);
            final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
            final OutputStream ops = editor.newOutputStream(0);
            if (ops != null) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        NetRequest okhttp = new NetRequest();
                        try {
                            final byte[] result = okhttp.request(url);
                            ops.write(result);
                            editor.commit();
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    listener.onData(result);
                                }
                            });
                        } catch (IOException e) {
                            e.printStackTrace();
                            try {
                                if (editor != null) {
                                    editor.abort();
                                }
                            } catch (IOException e1) {
                                e1.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面两个 和上一篇博客内容是一样的,下面就看一下行加了什么

LruCache 的封装

/**
 * @author Lv
 * Created at 2019/6/16
 */
@SuppressWarnings("WeakerAccess")
public class LruCachePhoto {
    /**
     * 图片 缓存技术的核心类,用于缓存下载好的所有图片,
     * 在程序内存达到设定值后会将最少最近使用的图片移除掉
     */
    private LruCache<String, Bitmap> mMenoryCache;

    public LruCachePhoto() {
        //获取应用最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        //设置 缓存文件大小为 程序最大可用内存的 1/8
        int cacheSize = maxMemory / 8;

        mMenoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };
    }
    /**
     * 从 LruCache 中获取一张图片,如果不存在 就返回 null
     * @param key LurCache 的键,这里是 图片的地址
     * @return 返回对应的 Bitmap对象,找不到则为 null
     */
    public Bitmap getBitmapFromMemoryCache(String key){
        return mMenoryCache.get(key);
    }

    /**
     *  添加一张图片
     * @param key key
     * @param bitmap bitmap
     */
    public void addBitmapToCache(String key,Bitmap bitmap){
            mMenoryCache.put(key,bitmap);
    }
}

AsynTask 的封装

package com.admin.utill.net.cache;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;

import com.admin.utill.net.NetRequest;


/**
 * @author Lv
 * Created at 2019/6/15
 */
@SuppressLint("StaticFieldLeak")
public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
    private LruCachePhoto mCachePhoto;
    private GridView mGridView;
    private DiskBitmapCache mDataCache;
    private ListView mListView;
    private ImageView mImageView;
    private Object mTag;

    private static final String TAG = "BitmapWorkerTask";

    /**
     * @param mCachePhoto 用于缓存下载好的图片,将图片缓存到内存
     * @param gridView    需要显示图片的 控件
     * @param Tag         每一个条目的 Tag
     */
    public BitmapWorkerTask(LruCachePhoto mCachePhoto, DiskBitmapCache dataCache, GridView gridView, Object Tag) {
        this.mGridView = gridView;
        init(mCachePhoto, dataCache, Tag);
    }

    public BitmapWorkerTask(LruCachePhoto mCachePhoto, DiskBitmapCache dataCache, ListView listView, Object tag) {
        this.mListView = listView;
        init(mCachePhoto, dataCache, tag);
    }

    public BitmapWorkerTask(LruCachePhoto mCachePhoto, DiskBitmapCache dataCache, ImageView imageView) {
        init(mCachePhoto, dataCache, null);
        this.mImageView = imageView;
    }

    private void init(LruCachePhoto mCachePhoto, DiskBitmapCache dataCache, Object tag) {
        this.mCachePhoto = mCachePhoto;
        this.mDataCache = dataCache;
        this.mTag = tag;
    }

    @Override
    protected Bitmap doInBackground(String... strings) {
        String imageUlr = strings[0];
        //获取内存的缓存
        Bitmap bitmap = mCachePhoto.getBitmapFromMemoryCache(imageUlr);
        if (bitmap!=null){
            return bitmap;
        }
        //判断本地是否有缓存
        if (!mDataCache.isCache(imageUlr)) {
            //没有缓存
            bitmap = downLoadBitmap(imageUlr);
        } else {
            byte[] bytes = mDataCache.readCache(imageUlr);
            bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
            mCachePhoto.addBitmapToCache(imageUlr, bitmap);
            return bitmap;
        }
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        super.onPostExecute(bitmap);
        ImageView imageView;
        if (mGridView != null) {
            imageView = mGridView.findViewWithTag(mTag);
        } else if (mListView != null) {
            imageView = mListView.findViewWithTag(mTag);
        }  else {
            imageView = this.mImageView;
        }
        if (imageView != null && bitmap!= null) {
            imageView.setImageBitmap(bitmap);
        }
    }
    private Bitmap downLoadBitmap(String imageUlr) {
        //如果没有缓存 就进行请求,然后进行缓存
        Bitmap bitmap = null;
        byte[] request = new NetRequest().request(imageUlr);
        if (request != null){
            bitmap = BitmapFactory.decodeByteArray(request, 0, request.length);
        }
        if (bitmap != null) {
            //将图片 缓存到内存
            mCachePhoto.addBitmapToCache(imageUlr, bitmap);
            //将图片 缓存到磁盘
            boolean b = mDataCache.writeData(imageUlr, bitmap);
            if (!b) {
                Log.e("PhotoCache", "磁盘缓存图片失败");
            }
            return bitmap;
        } else {
            return null;
        }
    }
}

加了一个 LruCache 和 一个异步任务,具体的注释都在上面了。

使用如下

在ListView 中使用

public class MainActivity extends PermissionCheck {

    private DiskBitmapCache mDisk;
    private LruCachePhoto mCache;

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCache = new LruCachePhoto();
        mDisk = new DiskBitmapCache(MainActivity.this, "456");

        final ListView listView = findViewById(R.id.listview);
        listView.setAdapter(new BaseAdapter() {
            @Override
            public int getCount() {
                return Images.imageThumbUrls.length;
            }

            @Override
            public Object getItem(int position) {
                return null;
            }

            @Override
            public long getItemId(int position) {
                return 0;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view;
                if (convertView == null) {
                    view = LayoutInflater.from(MainActivity.this).inflate(R.layout.photo_layout, null);
                } else {
                    view = convertView;
                }
                String url = Images.imageThumbUrls[position];
                ImageView imageView = view.findViewById(R.id.photo);
                imageView.setImageResource(R.drawable.log);
                imageView.setTag(url);
                BitmapWorkerTask task = new BitmapWorkerTask(mCache, mDisk, listView, url);
                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
                return view;
            }
        });

    }
}

​ 在上面给 缓存的文件夹名字起名为 456,然后就是一个ListView 了,注意,在getView 里面给 ImageView 设置了一个Tag ,这个非常重要,如果没有这个 Tag,我们将无法找到显示 ImageView ,还有 DiskBitmapCache 和 LruCachePhoto必须是全局的。

效果如下

在这里插入图片描述

在GridView 中使用

public class GridViewActvity extends AppCompatActivity {
    private DiskBitmapCache mDisk;
    private LruCachePhoto mCache;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_grid_view);
        mCache = new LruCachePhoto();
        mDisk = new DiskBitmapCache(this, "000");
        final GridView gridView= findViewById(R.id.gridView);
        gridView.setAdapter(new BaseAdapter() {
            @Override
            public int getCount() {
                return Images.imageThumbUrls.length;
            }

            @Override
            public Object getItem(int position) {
                return null;
            }

            @Override
            public long getItemId(int position) {
                return 0;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                View view;
                if (convertView == null) {
                    view = LayoutInflater.from(GridViewActvity.this).inflate(R.layout.photo_layout, null);
                } else {
                    view = convertView;
                }
                String url = Images.imageThumbUrls[position];
                ImageView imageView = view.findViewById(R.id.photo);
                imageView.setImageResource(R.drawable.log);
                imageView.setTag(url);
                BitmapWorkerTask task = new BitmapWorkerTask(mCache, mDisk, gridView, url);
                task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
                return view;
            }
        });
    }
}

使用方式一样,注意别忘了 Tag 和 DiskBitmapCache 和 LruCachePhoto必须是全局的。

效果如下

在这里插入图片描述
下面看一下缓存目录

在这里插入图片描述

本文的 图片使用的是 郭神博客中的图片:https://blog.csdn.net/guolin_blog/article/details/34093441

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tʀᴜsᴛ³⁴⁵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值