美文网首页
2018-07-07 Android ---> LruCache

2018-07-07 Android ---> LruCache

作者: 王培921223 | 来源:发表于2018-07-07 11:39 被阅读0次

     在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来。Android为我们提供了LruCache,今天我们就来学习这个缓存的知识以及原理。

    LruCache缓存的实例代码

    一、 我们建立一个简单的项目去体会LruCache的使用过程

    通过http请求网络上的图片文件,然后保存在缓存中。显示图片时,先从缓存中取,如果没有,就发送请求向服务器取。项目结构如下:

    二、 在AndroidManifest.xml文件中,加入网络权限的声明:

    三、 创建一个图片加载的类,用于对缓存的一些操作,重写LruCache的sizeOf方法:

    package com.example.linux.lrucachetest;

    import android.graphics.Bitmap;

    import android.util.LruCache;

    /**

    *Created by huhx on 2016/4/12.

    */

    public class ImageDownloader {

    private static final String TAG = "TextDownload";

    private LruCachelruCache;

    public ImageDownloader() {

    long maxMemory = Runtime.getRuntime().maxMemory();

    int cacheSize = (int) (maxMemory / 8);

    lruCache = new LruCache(cacheSize) {

                @Override

                protected int sizeOf(String key, Bitmap value) {

                    return value.getByteCount();

                }

            };

        }

        // 把Bitmap对象加入到缓存中

        public void addBitmapToMemory(String key, Bitmap bitmap) {

            if (getBitmapFromMemCache(key) == null) {

                lruCache.put(key, bitmap);

            }

        }

        // 从缓存中得到Bitmap对象

        public Bitmap getBitmapFromMemCache(String key) {

        Log.i(TAG, "lrucache size: " + lruCache.size());

            return lruCache.get(key);

        }

        // 从缓存中删除指定的Bitmap

        public void removeBitmapFromMemory(String key) {

            lruCache.remove(key);

        }

    }

    四、 在MainActivity中使用并测试LruCache:showBitmap方法是先从缓存中取,如果没有就发送http请求取得。

    public void showBitmap(View view) {

        Bitmap bitmap = imageDownloader.getBitmapFromMemCache("bitmap");

        if (bitmap == null) {

            new BitmapThread(bitmapUrl).start();

        } else {

            imageView.setImageBitmap(bitmap);

        }

    }

    五、 BitmapThread的线程:从服务器拿到Bitmap对象,并加入到缓存中。

    class BitmapThread extends Thread {

        private String bitmapUrl;

        BitmapThread(String bitmapUrl) {

            this.bitmapUrl = bitmapUrl;

        }

        @Override

        public void run() {

            Log.i(TAG, "run: " + Thread.currentThread().getName());

            Bitmap bitmap = null;

            HttpURLConnection connection = null;

            InputStream inputStream = null;

            try {

                URL url = new URL(bitmapUrl);

                connection = (HttpURLConnection) url.openConnection();

                connection.setConnectTimeout(5000);

                connection.setRequestMethod("GET");

                if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {

                    inputStream = connection.getInputStream();

                    bitmap = BitmapFactory.decodeStream(inputStream);

                }

                imageDownloader.addBitmapToMemory("bitmap", bitmap);

                handler.obtainMessage(DOWNLOAD_IMAGE, bitmap).sendToTarget();

            } catch (Exception e) {

                e.printStackTrace();

            } finally {

                if (connection != null) {

                    connection.disconnect();

                }

                if (inputStream != null) {

                    try {

                        inputStream.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

    }

    六、 handler处理消息,并显示图片:

    private Handler handler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            Log.i(TAG, "hanlder handleMessage: " + Thread.currentThread().getName());

            switch (msg.what) {

                case DOWNLOAD_IMAGE:

                    imageView.setImageBitmap((Bitmap) msg.obj);

                    break;

            }

        }

    };

    七、 从缓存中删除图片:

    publicvoid remove(View view) {

        imageDownloader.removeBitmapFromMemory("bitmap");

    }

    LruCache缓存的原理分析

    通过上述的案例,我们已经知道了LruCache的使用方法。接下来,我们一步步的分析它的过程以及原理。

    一、 LruCache的文档描述如下:

    A cache that holds strong references to a limited number of values. Each time a valueisaccessed, itismoved to the head of a queue. When a valueisadded to a full cache, the value at the end of that queueisevicted and may become eligibleforgarbage collection.

    二、 它的属性一方法说明如下:

    publicclassLruCache {

        privatefinalLinkedHashMap map;

        /** Size of this cache in units. Not necessarily the number of elements. */privateint size;

        privateint maxSize;

        privateint putCount;

        privateint createCount;

        privateint evictionCount;

        privateint hitCount;

        privateint missCount;

    }

    文档上一些对LruCache方法的描述:

    If your cached values hold resources that need to be explicitly released,overrideentryRemoved(boolean, K, V, V)

    If a cache miss should be computed on demand

    forthe corresponding keys,overridecreate(K). This simplifies the calling code, allowing it to assume a value will always be returned, even when there's a cache miss.

    Bydefault, the cache sizeismeasuredinthe number of entries. Override sizeOf(K, V) to size the cacheindifferent units. For example,thiscacheislimited to 4MiB of bitmaps:

    三、 LruCache只有一个构造方法,LruCache(int maxSize)代码如下:初始化一个LinkedHashMap

    publicLruCache(int maxSize) {

        if(maxSize <= 0) {

            thrownewIllegalArgumentException("maxSize <= 0");

        }

        this.maxSize = maxSize;

        this.map =newLinkedHashMap(0, 0.75f,true);

    }

    四、 LruCache的put方法是把内容放入到缓存中去,代码如下:

    publicfinal V put(K key, V value) {

        if(key ==null|| value ==null) {

            thrownewNullPointerException("key == null || value == null");

        }

        V previous;

        synchronized(this) {

            putCount++;

            size += safeSizeOf(key, value);

            previous = map.put(key, value);

            if(previous !=null) {

                size -= safeSizeOf(key, previous);

            }

        }

        if(previous !=null) {

            entryRemoved(false, key, previous, value);

        }

        trimToSize(maxSize);

        return previous;

    }

    其中safeSizeOf方法,是计算LruCache的已经缓存的大小,以下的sizeOf(默认返回1)方法是我们要重写的。

    privateint safeSizeOf(K key, V value) {

        intresult = sizeOf(key, value);

        if(result < 0) {

            thrownewIllegalStateException("Negative size: " + key + "=" + value);

        }

        return result;

    }

    我们要重写sizeOf方法:

    protectedint sizeOf(K key, V value) {

        return1;

    }

    五、 LruCache的get方法是从缓存中去取得内容,代码如下:

    publicfinal V get(K key) {

        if(key ==null) {

            thrownewNullPointerException("key == null");

        }

        V mapValue;

        synchronized(this) {

    // 如果根据相应的key得到value,就增加一次命中hitCount,并且返回结果        mapValue

    = map.get(key);

            if(mapValue !=null) {

                hitCount++;

                returnmapValue;        }

    // 否则增加一次missCountmissCount

    ++;

        }

        /*    * Attempt to create a value. This may take a long time, and the map

        * may be different when create() returns. If a conflicting value was

        * added to the map while create() was working, we leave that value in

        * the map and release the created value.

        *///试图根据这个key,创建一个value。这里的create(key)默认是返回null,当然这个方法是可以重写的V createdValue= create(key);

        if(createdValue ==null) {

            returnnull;

        }

        synchronized(this) {

            createCount++;

    //如果我们重写了create(key)方法而且返回值不为空,那么将上述的key与这个返回值写入到map当中mapValue

    = map.put(key, createdValue);

            if(mapValue !=null) {

                //There was a conflict so undo that last put

    // 方法放入最后put的key,value值

                map.put(key, mapValue);

            } else {

                size += safeSizeOf(key, createdValue);

            }

        }

        if(mapValue !=null) {

    // 这个方法也可以重写        entryRemoved(

    false, key, createdValue, mapValue);

            return mapValue;

        } else {

            trimToSize(maxSize);

            return createdValue;

        }

    }

    六、 LruCache的remove方法是从缓存中去删除内容,并更新已经缓存的大小,代码如下:

    publicfinal V remove(K key) {

        if(key ==null) {

            thrownewNullPointerException("key == null");

        }

        V previous;

        synchronized(this) {

            previous = map.remove(key);

            if(previous !=null) {

                size -= safeSizeOf(key, previous);

            }

        }

        if(previous !=null) {

            entryRemoved(false, key, previous,null);

        }

        return previous;

    }

    相关文章

      网友评论

          本文标题:2018-07-07 Android ---> LruCache

          本文链接:https://www.haomeiwen.com/subject/doxwuftx.html