android三级缓存访问网络图片

android三级缓存访问网络图片


什么是三级缓存?


第一级:内存缓存(优先从内存中加载图片,速度最快,不浪费流量)


第二级:本地缓存(其次从本地加载图片,速度快,不浪费流量)


第三级:网络缓存(最后从网络加载图片,速度慢,浪费流量)

简单原理图:

三级缓存分别的解决方案


内存缓存解决方案:

方案一:普通的hashmap<String ,Bitmap>。由于android默认给每个app只分配16M的内存。非常容易oom。强烈不建议!!!

方案二:软引用或者弱引用包装的Bitmap(SoftReference<Bitmap> softReference = mMemoryCache.get(url))

咋一看是不是就完美解决了呢?在api level 9之前确实是完美的。但是注意,android2.3以后,系统会优先将SoftReference,WeakReference回收,即使内存足够用。

  • java中的引用
    • 强引用 垃圾回收器不会回收,java默认引用都是强引用
    • 软引用 SoftReference 在内存不够时,会考虑回收
    • 弱引用 WeakReference 在内存不够时,会优先回收
    • 虚引用 PhantomReference 在内存不够时,最优先回收

方案三:lruCache(Lru: least recentlly used 最近最少使用算法)可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围。

本地缓存解决方案:

通过把图片写到sd卡解决,没什么可讲的。

网络缓存解决方案:

利用AsyncTask来解决这个问题,其实AsyncTask就是线程池加上handler,实现异步加载任务和主界面的ui任务更新(线程池和handler以后有机会再详细讲)。

AsyncTask只有两个核心方法doInBackground(Object...params)和onPostExecute(Bitmap result)

doInBackgound(Object...params)是运行在子线程,所以可以直接进行异步任务加载,就是访问网络加载图片,在这里面参数是ImageView对象和图片url

onPostExecute(Bitmap result)是运行在主线程(查看源码得知该方法运行在finish()方法,而finish()是运行在handler里面),所以可以直接更新UI,参数是返回的bitmap对象。

三级缓存代码:

内存缓存代码:

public class MemoryCacheUtil {
    private LruCache<String, Bitmap> mMemoryCache;

    public MemoryCacheUtil() {
        // LruCache 可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围
        // Lru: least recentlly used 最近最少使用算法
        long maxMemory = Runtime.getRuntime().maxMemory();// 获取分配给app的内存大小
        System.out.println("maxMemory:" + maxMemory);

        mMemoryCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
            // 返回每个对象的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // int byteCount = value.getByteCount();
                int byteCount = value.getRowBytes() * value.getHeight();// 计算图片大小:每行字节数*高度
                return byteCount;
            }
        };
    }

    /**
     * 写缓存
     */
    public void setMemoryCache(String url, Bitmap bitmap) {
        // mMemoryCache.put(url, bitmap);
        // SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);//
        // 使用软引用将bitmap包装起来
        // mMemoryCache.put(url, soft);
        mMemoryCache.put(url, bitmap);
    }

    /**
     * 读缓存
     */
    public Bitmap getMemoryCache(String url) {
        // SoftReference<Bitmap> softReference = mMemoryCache.get(url);
        //
        // if (softReference != null) {
        // Bitmap bitmap = softReference.get();
        // return bitmap;
        // }

        return mMemoryCache.get(url);
    }
}

本地缓存代码:

public class LocalCacheUtil {
    private static final String LOCAL_CACHE_PATH=
            Environment.getExternalStorageDirectory().getAbsolutePath()+"/lry_cache";

    public void setLocalCache(String url, Bitmap bitmap){
        File dir=new File(LOCAL_CACHE_PATH);
        if(!dir.exists()||!dir.isDirectory()){
            dir.mkdirs();
        }
        try {
            //因为url里面可能包含特殊字符,所以可能创建不了文件
            String fileName=MD5Encoder.encode(url);
            //创建一个在文件夹dir下的文件
            File cacheFile=new File(dir,fileName);
            //写到本地  格式,质量(0-100),输出流
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(cacheFile));

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Bitmap getLocalCache(String url){
        try {
            File cacheFile=new File(LOCAL_CACHE_PATH,MD5Encoder.encode(url));
            if(cacheFile.exists()){
                return BitmapFactory.decodeStream(new FileInputStream(cacheFile));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

网络缓存代码:

public class NetCacheUtil {
    private LocalCacheUtil localCacheUtil;
    private MemoryCacheUtil memoryCacheUtil;
    public NetCacheUtil(LocalCacheUtil localCacheUtil,MemoryCacheUtil memoryCacheUtil) {
        this.localCacheUtil=localCacheUtil;
        this.memoryCacheUtil=memoryCacheUtil;
    }

    public void getBitmapFromNet(ImageView imageView, String url){
        //AsyncTask 异步封装的工具 实现异步请求和主界面的更新(线程池+handler)
        new BitmapTask().execute(imageView,url);
    }

    /**
     * 三个泛型的意义
     * 第一个泛型代表的是doInBackground(Object...params)里面的参数,是请求的参数
     * 第二个泛型的意义是onProgressUpdate()里的参数类型,更新进度用的
     * 第三个泛型的意义是onPostExecute()里的参数类型以及doInBackground的返回值类型,是请求的结果
     */
    class BitmapTask extends AsyncTask<Object,Integer,Bitmap>{
        private ImageView imageView;
        private String url;
        /**
         * 预加载,在异步请求之前做一些事情,比如弹出dialog,运行在主线程
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         * 正在加载,运行在子线程,可以直接异步请求
         * @param params
         * @return
         */
        @Override
        protected Bitmap doInBackground(Object... params) {
            imageView = (ImageView) params[0];
            url = (String) params[1];
            imageView.setTag(url);

            //根据图片url异步下载网络图片设置在imageview上面
            Bitmap bitmap = download(url);
//            publishProgress(values);如果自己一个一个字节得下载图片,可以获取下载进度并且调用此方法更新进度(回调onProgressUpdate)
            return bitmap;
        }

        /**
         * 更新进度,运行在主线程,可以直接更新UI,该方法在handler里面调用的
         * @param values
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        /**
         * 加载结束,运行在主线程,可以直接更新UI,看源码知该方法是在finish()里面调用,而finish()是在handler调用的
         * 所以可以直接更新UI
         * @param result
         */
        @Override
        protected void onPostExecute(Bitmap result) {
            if(result!=null){
                String tagUrl=(String)imageView.getTag();
                if(url.equals(tagUrl)){
                    imageView.setImageBitmap(result);

                    //写本地缓存
                    localCacheUtil.setLocalCache(url,result);
                }
            }
            super.onPostExecute(result);
        }
    }

    /**
     * 下载图片
     * @param url 图片地址
     * @return itmapB对象
     */
    private Bitmap download(String url) {
        HttpURLConnection conn=null;
        try {
             conn= (HttpURLConnection) new URL(url).openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);//连接超时 5s没有连接上服务器
            conn.setReadTimeout(5000);//读取超时  5s未读取到数据
            conn.connect();
            int responseCode=conn.getResponseCode();
            if(responseCode==200){
                InputStream is=conn.getInputStream();
                Bitmap bitmap= BitmapFactory.decodeStream(is);

                return bitmap;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(conn!=null){
                conn.disconnect();
            }
        }
        return null;
    }
}
核心:

public class MyBitmapUtil {
    private NetCacheUtil netCacheUtil;
    private LocalCacheUtil localCacheUtil;
    private MemoryCacheUtil memoryCacheUtil;

    public MyBitmapUtil(){
        localCacheUtil=new LocalCacheUtil();
        memoryCacheUtil=new MemoryCacheUtil();
        netCacheUtil=new NetCacheUtil(localCacheUtil,memoryCacheUtil);
    }

    /**
     *
     * @param imageView ImageView
     * @param url 网络图片地址
     */
    public void display(ImageView imageView,String url){
        display(imageView,url,null);
    }

    /**
     *
     * @param imageView
     * @param url
     * @param bitmap 设置默认图片
     */
    public void display(ImageView imageView, String url, Bitmap bitmap){
        if(bitmap!=null){
            imageView.setImageBitmap(bitmap);//设置默认图片
        }
        //三级缓存
        //内存缓存 优先从内存中加载图片, 速度最快, 不浪费流量
        Bitmap memoryBitmap=memoryCacheUtil.getMemoryCache(url);
        if(memoryBitmap!=null){
            imageView.setImageBitmap(memoryBitmap);
            Log.i("TAG","从内存读取");
            return;
        }
        //本地缓存 其次从本地(sdcard)加载图片, 速度快, 不浪费流量
        Bitmap localBitmap=localCacheUtil.getLocalCache(url);
        if(localBitmap!=null){
            imageView.setImageBitmap(localBitmap);
            //写内存缓存
            memoryCacheUtil.setMemoryCache(url,localBitmap);
            Log.i("TAG","从本地读取");
            return;
        }
        //网络缓存 最后从网络下载图片, 速度慢, 浪费流量
        netCacheUtil.getBitmapFromNet(imageView,url);
        Log.i("TAG","从网络读取");
    }
}

防止url带特殊符号建不了文件的md5工具类

public class MD5Encoder {
	
	public static String encode(String string) throws Exception {
	    byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
	    StringBuilder hex = new StringBuilder(hash.length * 2);
	    for (byte b : hash) {
	        if ((b & 0xFF) < 0x10) {
	        	hex.append("0");
	        }
	        hex.append(Integer.toHexString(b & 0xFF));
	    }
	    return hex.toString();
	}
}
如何使用样例代码:

public class MyBitmapActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_bitmap);

        Bitmap default_bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
        ImageView imageview= (ImageView) findViewById(R.id.imageview);
        new MyBitmapUtil().display(imageview,"http://7mno4h.com2.z0.glb.qiniucdn.com/5608eb8cN9b9a0a39.jpg",default_bitmap);
                
    }
}

以上就是本人对于三级网络缓存的全部理解,如有不当的地方,还请多多指教!qq:953506233


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值