记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 toEvict =map.nxt; 代替 代码片段2即可;

参考

AndroidStudio debug source code和运行代码不匹配

阅读Android源码的一些姿势 - 中二病也要开发ANDROID - SegmentFault 思否\

framework java

lruCache- pie-release

link

你可能感兴趣的:(android)