編輯:關於Android編程
在我們的手機應用開發時候,我們經常會遇到大數據訪問的時候,我們通常會考慮以下幾個方面的情況。一、手機內存的限制還必須保證應用反應的流暢;二、盡量小的流量消耗,不然,你的應用流暢度再好體驗再好,用戶還是會毫不猶豫的卸載掉你的應用。大數據量訪問的情況下,數據緩存是我們一定會考慮到的解決方案。而作為緩存,我們很重要的會考慮以下幾點:1.訪問速度;2.逐出舊的緩存策略;3.最好還能考慮到一定的並發度。這篇我們主要說說LRU策略的緩存算法實現,我們就用圖片緩存為例來談談Android應用開發中的緩存實現。
首先我們來看看谷歌官方的推薦的緩存:在Android3.0加入的LruCache和 DiskLruCache(硬盤緩存結構)類。我們從代碼的實現知道,LruCache和DiskLruCache緩存的實現都是基於JDK的LinkedHashMap集合來實現。下面我們來從LinkedHashMap的源碼的分析來開始學習。
通過源碼我們知道,LinkedHashMap是繼承HashMap,底層結構不僅使用HashMap來保存元素,同時通過繼承HashMapEntry 實現雙向鏈表的結構來關聯其他的元素。我們先來看LInkedHashMap的節點實現:
/**
* LinkedHashMap entry.
*/
private static class Entry extends HashMap.Entry {
// These fields comprise the doubly linked list used for iteration.
Entry before, after; LinkedHashMap的節點Entry繼承自HashMap.Entry來實現,增加兩個引用來分別指向前一個元素和後一個元素。LinkedHashMap的實現是在HashMap的基礎上增加雙鏈表的結構。
我們再來看看LinkedHashMap的初始化,我們看到構造函數參數最多的情況:
/**
* Constructs an empty LinkedHashMap instance with the
* specified initial capacity, load factor and ordering mode.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @param accessOrder the ordering mode - true for
* access-order, false for insertion-order
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder; //accessOrder指定排序,默認為false,為fasle的時候,插入順序排序,為true時候,訪問順序排序
}我們看到重寫的基類的初始化方法init實習:/**
* Called by superclass constructors and pseudoconstructors (clone,
* readObject) before any entries are inserted into the map. Initializes
* the chain.
*/
void init() {
header = new Entry(-1, null, null, null);
header.before = header.after = header;
} 此處初始化頭指針給循環指定。下面我們重點來看看集合最總要的兩個方法put和get的實現。我們先看put方法,LinkedHashMap的put方法並沒有重寫HashMap的put方法。只是重寫了put方法下的addEntry方法,addEntry方法執行時候是當LinkedHashMap插入新的結點的時候執行。/**
* This override alters behavior of superclass put method. It causes newly
* allocated entry to get inserted at the end of the linked list and
* removes the eldest entry if appropriate.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed, else grow capacity if appropriate
Entry eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
}
/**
* This override differs from addEntry in that it doesn't resize the
* table or remove the eldest entry.
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry old = table[bucketIndex];
Entry e = new Entry(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
} 我們看我們產生新結點的方法creatEntry實現,我們先給找到哈希表上對應的位置,然後給新的Entry指定前後的節點。執行方法e.addBefore(header)代碼如下: /**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
} 我們看到代碼實現的功能就是把新結點添加到header結點前。我們再回到addEntry的代碼實現,我們看 Entryprotected boolean removeEldestEntry(Map.Entryeldest) { return false; }
removeEldestEntry方法執行默認返回false,意思就是我們LinkedHashMap默認不會逐去舊的節點。我們可以通過在子類重載來重寫removeEldestEntry的實現,來修改LRU策略。下面我們用圖示來表示put方法的執行一下代碼的執行過程:
map.put("22", "xxx");
首先map執行前的結構是這樣:

為了不讓圖上密密麻麻的都是引用箭頭亂竄,我省略了HashMap的很多Entry結點的before和after的指向,我們只是重點表示header的引用指向。我們執行方法map.put("22","xxx")後的數據結構變成這樣:

我們把key為22的節點通過hash算法,指定到eee的後面,同時我們把header的before指向修改為指向key為22的節點。分析完我們的put方法,我們接著來分析get方法的實現,代碼如下:
public V get(Object key) {
Entry e = (Entry)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
} 我們看到get方法很簡單,首先我們去Map取key對應的value是否存在,如果不存在,我們返回null。如果存在,我們執行e.recoedAccess(this)方法。 /**
* This method is invoked by the superclass whenever the value
* of a pre-existing entry is read by Map.get or modified by Map.set.
* If the enclosing Map is access-ordered, it moves the entry
* to the end of the list; otherwise, it does nothing.
*/
void recordAccess(HashMap m) {
LinkedHashMap lm = (LinkedHashMap)m;
if (lm.accessOrder) { //此處的判斷LinkedHashMap的排序順序
lm.modCount++;
remove();
addBefore(lm.header);
}
}
recodeAccess方法中,當我們的排序順序為false,為按插入排序順序的時候,我們直接退出方法。當我們的排序順序為訪問排序順序的時候,我們執行remove方法,把結點從鏈表裡切除出來,然後執行addBefore方法時候,我們把結點插入到header結點的前面。這樣,我們就實習每次根據訪問位置來排序的操作。下面我們通過圖的例子來說明,當LinkedHashMap的排序為訪問排序的時候,我們get操作的過程,LinkedHashMap的數據結構的變化。我們執行的代碼如下:
put.get("15");//我們假設key為15對應的value為"bbb"我們在執行完map.put("22","xxx")後,再執行put.get("15")的變化如下:

以上為執行get方法之後的代碼發生變化,"bbb”對應的節點其實沒有發生變化,只是在鏈表結構中的指針發生了變化,因此對應的迭代器訪問的時候,我們讀取位置不一樣。
因為我們可以知道我們LRU的節點肯定是在header的after指向節點。當我們重寫removeEldestEntry來修改LRU策略的時候,我們也是首先逐出header的after所指向的節點。然後,我們最後來看LinkedHashmap的迭代器的實現如下:
private abstract class LinkedHashIterator從LinkedHashMap實現的實現的迭代器內部類LinkedHashIterator看,我們迭代器的元素訪問順序是從header節點開始往after方向開始迭代。以上為對LinkedHashMap的分析簡單分析,下一節我們會針對LinkedHashMap的數據結構的特性來說說,用LinkedHashMap結構實現緩沖的快速訪問的優勢,還會結合常用的開源實現,來說說實現一定並發的Android下緩存實現。implements Iterator { Entry nextEntry = header.after; Entry lastReturned = null; /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount; public boolean hasNext() { return nextEntry != header; } public void remove() { if (lastReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); LinkedHashMap.this.remove(lastReturned.key); lastReturned = null; expectedModCount = modCount; } Entry nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == header) throw new NoSuchElementException(); Entry e = lastReturned = nextEntry; nextEntry = e.after; //此處我們可知我們的迭代器實現訪問節點的after引用指向的節點。 return e; } }
Objective-C中的Runtime
前言Runtime是一套比較底層的純C語言API,包含了很多底層的C語言API。在我們平時編寫的OC代碼中,程序運行時,其實最終都是轉成了Runtime的C語言代碼。Ru
翻翻git之---一個豐富的通知的工具庫 NotifyUtil
P2 正菜環節今天上的是一個通知的工具庫,作者寫的比較全,使用起來頁比較方便,而且內容少,直接Copy就好了。(國內很多廠商有自己的ROM定制,還有些野生大牛的自己創作,
Android自定義豎直方向SeekBar
寫在前面因為有這樣的一個場景,需要實現豎直方向的多色進度條,然後在網上也找了下,沒看到符合需要的,於是自定義了一個,效果如下:具體實現本來想定義水平的,然後旋轉一下,後來
Android增量升級簡單實現(附源碼)
隨著現在手機硬件不斷的提升,分辨率提高手機的安裝包也是越來越大了。當年NOKIA,MOTO時代,一個手機APP如果有1MB那都是算大的,2MB已經不得了了。雖然網