前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写图片缓存框架 ImageLoader

手写图片缓存框架 ImageLoader

作者头像
老马的编程之旅
发布2022-06-22 09:52:10
8580
发布2022-06-22 09:52:10
举报
文章被收录于专栏:深入理解Android

图片缓存是App开发中最常见的,本篇博文给大家带来自己手写的图片缓存框,大致的思路很简单,首先从内存中获取图片,如果内存中没有,就从手机本地进行获取,如果还没有,就从网络访问进行获取。 所以,我们在ImageLoader中只需要暴露一个方法loadImage(),外部只需要调用这个方法就可以完成图片缓存的所以逻辑

代码语言:javascript
复制
//加载图片到对应的控件
public void loadImage(String key, ImageView view) {

    synchronized (view) {
        this.imageView = view;
        //检查缓存里是否有
        Bitmap bitmap = getFromCache(key);

        if (bitmap != null) {

            //缓存存在,直接显示
            view.setImageBitmap(bitmap);
        } else {
            //网络进行下载
                /*view.setBackgroundDrawable(drawable);*/
            view.setBackgroundDrawable(new ColorDrawable(Color.GRAY));

            ImageAsycTask task = new ImageAsycTask(view);
            task.execute(key);
        }
    }

}

这里,我将从内存中和本地获取图片的逻辑都统一放在getFromCache()方法中,这里值得一提的是,当内存中没有,本地有该图片的时候,还会将这个图片放入LinkedHashMap中,让这个图片在LinkedHashMap中处于最新的位置,不至于被回收。

代码语言:javascript
复制
private Bitmap getFromCache(String key) {
    //检查内存软引用
    synchronized (firstHashMap) {
        if (firstHashMap.get(key) != null) {
            Bitmap bitmap = firstHashMap.get(key).get();
            if (bitmap != null) {
                //更新一下,因为Lru算法会默认清除最老的选项
                firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
                return bitmap;
            }
        }
    }
    //检查磁盘
    Bitmap bitmap = getFromLocal(key);
    if (bitmap != null) {
        //更新一下,因为Lru算法会默认清除最老的选项
        firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
        return bitmap;
    }
    return null;
}

在内存中,我使用了一个LinkedHashMap

代码语言:javascript
复制
private static LinkedHashMap<String, SoftReference<Bitmap>> firstHashMap = new LinkedHashMap<String, SoftReference<Bitmap>>(MAX_LENGTH) {
    @Override
    protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
        if (this.size() > MAX_LENGTH) {
            //返回true,表示移除最老的
            return true;
        }
            //往磁盘进行添加
            diskCache(eldest.getKey(), eldest.getValue());
            return false;
    }

};

这里内部的removeEldestEntry()方法内部如果返回true,会默认移除掉最旧的一个成员,返回false表示不移除,同时还会把图片放入到手机本地中,这个逻辑通过diskCache()方法实现的,这里图片在本地中名字使用md5加密后的名字

代码语言:javascript
复制
//  把图片缓存到本地磁盘
private static void diskCache(String key, SoftReference<Bitmap> value) {
    //消息摘要算法
    Bitmap bitmap;
    FileOutputStream os = null;
    try {
        String fileName = MD5Utils.md5(key, "utf-8");
        String path = mContext.getCacheDir().getAbsolutePath() + File.separator + fileName;

        os = new FileOutputStream(new File(path));
        if (value.get() != null) {
            value.get().compress(Bitmap.CompressFormat.JPEG, 80, os);

        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

如果本地缓存中没有,会通过getFromLocal(key)方法,从手机本地中进行获取

代码语言:javascript
复制
//检查sd卡里是否有
private Bitmap getFromLocal(String key) {
    InputStream is = null;
    try {
        String filname = MD5Utils.md5(key, "utf-8");
        if (filname == null) {
            return null;

        } else {
            String path = mContext.getCacheDir().getAbsolutePath() + File.separator + filname;
            is = new FileInputStream(new File(path));
            Bitmap bitmap = BitmapFactory.decodeStream(is);
            return bitmap;
        }
    } catch (Exception e) {
        e.printStackTrace();
        return null;

    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

如果本地和内存都没有的话,那么就从网络进行获取,这里使用了AsyncTask

代码语言:javascript
复制
class ImageAsycTask extends AsyncTask<String, Void, Bitmap> {
    private ImageView imagView;
    private String key;

    public ImageAsycTask(ImageView imageView) {
        this.imagView = imageView;
    }



    @Override
    protected Bitmap doInBackground(String... strings) {
        this.key = strings[0];
        Log.i(TAG,key);
        Bitmap bitmap = downLoad(key);

        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {

        super.onPostExecute(bitmap);
        if (bitmap != null) {
            addCache(key, bitmap);
            /*Log.i("11",bitmap.toString());*/
            imagView.setImageBitmap(bitmap);
        }
    }
}

其中downLoad()方法就是访问网络获取图片的方法

代码语言:javascript
复制
private Bitmap downLoad(String key) {
    final Bitmap[] bitmap = new Bitmap[1];
    mHttpClient = new OkHttpClient();
    Request request = new Request.Builder().url(key).build();
    mHttpClient.newCall(request).enqueue(new Callback() {



        @Override
        public void onFailure(Call call, IOException e) {
            Log.i("TAG", "网络访问失败了");

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            InputStream is = response.body().byteStream();
            bitmap[0] = BitmapFactory.decodeStream(is);
            Log.d("okHttp", bitmap[0].toString());
                   }
    });
    return bitmap[0];


}

图片下载完成之后,我们会将其读写到内存中,并显示在view上,这个view是通过AsyncTask的构造函数传进来的

代码语言:javascript
复制
private void addCache(String key, Bitmap bitmap) {
    if (bitmap != null) {
        synchronized (firstHashMap) {
            firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
        }
    }
}

这样这个图片缓存框架就写好了,我们就单纯的在MainActivity中访问网络进行显示来验证我们的框架,布局太简单就不贴了

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {
    String url = "http://7mno4h.com2.z0.glb.qiniucdn.com/560bd9b6Nc4b5cbfe.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView  = (ImageView) findViewById(R.id.image);
        ImageLoader imageLoader = ImageLoader.getmInstance(this);
        imageLoader.loadImage(url,imageView);
    }
}

运行项目,效果如下:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-07-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档