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

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

LruCache缓存的实例代码

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

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


2018-07-07 Android ---> LruCache的使用及原理_第1张图片

二、 在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的使用及原理)