2018-07-07 Android ---> LruCache的使用及原理

 在你应用程序的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;

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容