編輯:Android編程入門
在你應用程序的UI界面加載一張圖片是一件很簡單的事情,但是當你需要在界面上加載一大堆圖片的時候,情況就變得復雜起來。Android為我們提供了LruCache,今天我們就來學習這個緩存的知識以及原理。
目錄導航
一、 我們建立一個簡單的項目去體會LruCache的使用過程,
通過http請求網絡上的圖片文件,然後保存在緩存中。顯示圖片時,先從緩存中取,如果沒有,就發送請求向服務器取。項目結構如下:
二、 在AndroidManifest.xml文件中,加入網絡權限的聲明:
<uses-permission android:name="android.permission.INTERNET"/>
三、 創建一個圖片加載的類,用於對緩存的一些操作:
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 LruCache<String, Bitmap> lruCache; public ImageDownloader() { long maxMemory = Runtime.getRuntime().maxMemory(); int cacheSize = (int) (maxMemory / 8); lruCache = new LruCache<String, Bitmap>(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; } } };
七、 從緩存中刪除圖片:
public void remove(View view) { imageDownloader.removeBitmapFromMemory("bitmap"); }
八、 輸出日志結果如下:
第一次點擊showBitmap:
04-12 19:51:21.768 15941-15941/com.example.linux.lrucachetest I/TextDownload: lrucache size: 0 04-12 19:51:21.771 15941-19434/com.example.linux.lrucachetest I/MainActivity: run: Thread-2264 04-12 19:51:21.816 15941-19434/com.example.linux.lrucachetest I/TextDownload: lrucache size: 0 04-12 19:51:21.817 15941-15941/com.example.linux.lrucachetest I/MainActivity: hanlder handleMessage: main
第二次點擊showBitmap:
04-12 19:52:11.128 15941-15941/com.example.linux.lrucachetest I/TextDownload: lrucache size: 256000
點擊remove之後,再點擊showBitmap:
04-12 19:52:47.834 15941-15941/com.example.linux.lrucachetest I/TextDownload: lrucache size: 0 04-12 19:52:47.839 15941-20689/com.example.linux.lrucachetest I/MainActivity: run: Thread-2266 04-12 19:52:47.895 15941-20689/com.example.linux.lrucachetest I/TextDownload: lrucache size: 0 04-12 19:52:47.895 15941-15941/com.example.linux.lrucachetest I/MainActivity: hanlder handleMessage: main
通過上述的案例,我們已經知道了LruCache的使用方法。接下來,我們一步步的分析它的過程以及原理。
一、 LruCache的文檔描述如下:
A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.
二、 它的屬性一方法說明如下:
public class LruCache<K, V> { private final LinkedHashMap<K, V> map; /** Size of this cache in units. Not necessarily the number of elements. */ private int size; private int maxSize; private int putCount; private int createCount; private int evictionCount; private int hitCount; private int missCount; }
文檔上一些對LruCache方法的描述:
If your cached values hold resources that need to be explicitly released, override entryRemoved(boolean, K, V, V)
If a cache miss should be computed on demand for the corresponding keys, override create(K). This simplifies the calling code, allowing it to assume a value will always be returned, even when there's a cache miss.
By default, the cache size is measured in the number of entries. Override sizeOf(K, V) to size the cache in different units. For example, this cache is limited to 4MiB of bitmaps:
三、 LruCache只有一個構造方法,LruCache(int maxSize)代碼如下:初始化一個LinkedHashMap
public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<K, V>(0, 0.75f, true); }
四、 LruCache的put方法是把內容放入到緩存中去,代碼如下:
public final V put(K key, V value) { if (key == null || value == null) { throw new NullPointerException("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)方法是我們要重寫的。
private int safeSizeOf(K key, V value) { int result = sizeOf(key, value); if (result < 0) { throw new IllegalStateException("Negative size: " + key + "=" + value); } return result; }
我們要重寫sizeOf方法:
protected int sizeOf(K key, V value) { return 1; }
五、 LruCache的get方法是從緩存中去取得內容,代碼如下:
public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } V mapValue; synchronized (this) {
// 如果根據相應的key得到value,就增加一次命中hitCount,並且返回結果 mapValue = map.get(key); if (mapValue != null) { hitCount++; return mapValue; }
// 否則增加一次missCount missCount++; } /* * 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) { return null; } 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方法是從緩存中去刪除內容,並更新已經緩存的大小,代碼如下:
public final V remove(K key) { if (key == null) { throw new NullPointerException("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; }
在Android5.0往後的平台上,你想通過單純的調用File.delete()或著ContentResolver.delete()來刪除Sdcard上的文件會刪除失敗。
安卓手機的圖形鎖包括3*3,4*4,5*5的點陣,按次序連接數個點從而達到鎖定/解鎖的功能。以3*3為例,最少需要連接4個點,最多
單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。 針對Android開發,目前網上有很多在Eclipse環境下進行單元測試的教程,然而
這周作業,要做一個類似QQ的左滑刪除效果的ListView,因為不想給每個item都放一個按鈕,所以決定用PopupWindow,這裡記錄一下先放一下效果圖:先說明一下這