手写图片缓存框架 ImageLoader

2022-06-22 09:52:10 浏览数 (1)

图片缓存是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);
    }
}

运行项目,效果如下:

0 人点赞