記Android SDK(28)中 lrucache的一個bug.md

平臺:
android-28(android 9.0)

問題描述:

我有個需求, 需要改動LruCache, 當我從android 9.0 SDK源碼(從AndrodiStudio SDK manager下載)拷貝到本地目錄後, 發現一個bug.

其TrimToSize 函數會淘汰最新元素(鏈尾), 而不是最老元素(鏈頭), 和鏈接5 提到的android5.0 中的bug一致. 奇怪的是, 直接調用系統的LruCache(android.util)不會有問題, 調用我從SDK源碼複製出來的代碼會出現上述問題.
測試代碼:

        SerializableLruCache<Integer, Integer> cache = new SerializableLruCache<Integer, Integer>(10);
        LruCache<Integer, Integer> lruCache = new LruCache<Integer, Integer>(10);
        for (int i = 0; i < 20; i++) {
            cache.put(i,i);
            lruCache.put(i,i);
        }
        for (Map.Entry entry : cache.snapshot().entrySet()) {
            System.out.print("," +  entry.getValue());
        }
        System.out.println();
        for (Map.Entry entry : lruCache.snapshot().entrySet()) {
            System.out.print("," + entry.getValue());
        }

前者打印10~ 19, 後者打印0 ~9

調試過程

當調試跳入SDK的LruCache源碼時會提示 Source code does not match the bytecode. 而我的真機, 工程的compileSDK, SDK源碼 都是android-28. 爲什麼還會提示源碼不匹配呢?

主要問題是trimToSize函數找待刪除元素的時候有差異;

在android 27 是這樣的:

        Map.Entry<K, V> toEvict = map.eldest();
//...
//eldest是被標記隱藏的. 實現如下:
    public Entry<K, V> eldest() {
        LinkedEntry<K, V> eldest = header.nxt;
        return eldest != header ? eldest : null;
    }

而android 28 源碼

                // BEGIN LAYOUTLIB CHANGE
                // get the last item in the linked list.
                // This is not efficient, the goal here is to minimize the changes
                // compared to the platform version.
                Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }

問題是, 在android9真機上對應的源碼是什麼. 使用jd-gui工具反編譯android9.0.jar看到具體實現是這樣的:


public void trimToSize(int maxSize) { throw new RuntimeException("Stub!"); }

這裏意思是androidSDK沒有實現此函數, 具體實現參考framework. SDK源碼註釋也說明需要對比平臺版本, 但是並沒有細說.

鏈接4是Android9.0 framework版本的LruCache實現, trimTosSize函數使用 map.eldest();

原因總結
原來是Framework實現和SDK源碼不一致, 而我複製了一份帶bug版本的LruCache.

解決方案

由於 map.eldest();是隱藏調用, 無法直接使用. 用 LinkedEntry<K, V> toEvict =map.nxt; 代替 代碼片段2即可;

參考

AndroidStudio debug source code和運行代碼不匹配

閱讀Android源碼的一些姿勢 - 中二病也要開發ANDROID - SegmentFault 思否\

framework java

lruCache- pie-release

link

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章