美文网首页
Glide的DataLoadProviderRegistry类的

Glide的DataLoadProviderRegistry类的

作者: android_hcf | 来源:发表于2019-07-23 13:40 被阅读0次

      今天研究Glide源码时看到了DataLoadProviderRegistry中的取值操作。先让大家看下该代码:

    public class DataLoadProviderRegistry {
        private static final MultiClassKey GET_KEY = new MultiClassKey();
    
        private final Map<MultiClassKey, DataLoadProvider<?, ?>> providers =
                new HashMap<MultiClassKey, DataLoadProvider<?, ?>>();
    
        public <T, Z> void register(Class<T> dataClass, Class<Z> resourceClass, DataLoadProvider<T, Z> provider) {
            providers.put(new MultiClassKey(dataClass, resourceClass), provider);
        }
    
        @SuppressWarnings("unchecked")
        public <T, Z> DataLoadProvider<T, Z> get(Class<T> dataClass, Class<Z> resourceClass) {
            DataLoadProvider<?, ?> result;
            synchronized (GET_KEY) {
                GET_KEY.set(dataClass, resourceClass);
                result = providers.get(GET_KEY);
            }
            if (result == null) {
                result = EmptyDataLoadProvider.get();
            }
            return (DataLoadProvider<T, Z>) result;
        }
    }
    

      可以看出,register中的key是通过new得到的,但是在get方法中取值却是通过GET_KEY这个常量获取到的。通常我们都会认为,因为put时没有用过该key,返回的一定是null,但是实际上的的确确获取到了之前的值,这是为什么?
      先不解释原因,先看下是不是MultiClassKey这个类的原因呢?

    public class MultiClassKey {
        private Class<?> first;
        private Class<?> second;
    
        public MultiClassKey() {
            // leave them null
        }
    
        public MultiClassKey(Class<?> first, Class<?> second) {
            set(first, second);
        }
    
        public void set(Class<?> first, Class<?> second) {
            this.first = first;
            this.second = second;
        }
    
        @Override
        public String toString() {
            return "MultiClassKey{"
                    + "first=" + first
                    + ", second=" + second
                    + '}';
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
    
            MultiClassKey that = (MultiClassKey) o;
    
            if (!first.equals(that.first)) {
                return false;
            }
            if (!second.equals(that.second)) {
                return false;
            }
    
            return true;
        }
    
        @Override
        public int hashCode() {
            int result = first.hashCode();
            result = 31 * result + second.hashCode();
            return result;
        }
    }
    

      可以看出其重写了equals和hashCode,用来保证“它”就是我。HashMap是不是通过判断equals和hashCode来找到“我”,然后映射到对应的value值?
      答案是肯定的,了解HashMap原理的人都知道,其取值过程正是如此:

    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
    

    这里着重要看两点,即HashMap取值原理:

    // 1,通过(n-1)&hash得出key在HashMap中的索引位置
    Node<K,V> first = tab[(n - 1) & hash]
    
    // 2,通过判断节点的hash值以及equals的异同来判断是否找到对应key
    if (first.hash == hash && // always check first node
        ((k = first.key) == key || (key != null && key.equals(k))))
        return first;
    

      这就是是文章开头的部分为什么可以通过GET_KEY来获取到之前的取值了,实际上正是HashMap通过key值的hash值和equals的异同来找到对应value。

      当然通过该两步也不一定就可以找到指定value,对于HashMap的put出现冲突时就会轮询判断node的next的hashCode和equals的异同来决定是覆盖还是存到next当中,所以get也就是同样道理,不断轮询判断next的hashCode和equals方法的异同来判断是否已找到指定value:

     if ((e = first.next) != null) {
        // 如果链表长度超过8个,则通过红黑树的结构存储数据,提高数据的访问速度
        if (first instanceof TreeNode)
            return ((TreeNode<K,V>)first).getTreeNode(hash, key);
        do {
            if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        } while ((e = e.next) != null);
    }
    

    下边是题外话

      原因以及原理都已经分析过了。大家有没有想过为什么文章开头的DataLoadProviderRegistry这个类非要用这种操作呢?通常在get中直接把key传过来不就完事了吗?

      为什么?

      好了,大家再看下DataLoadProviderRegistry的get传参以及MultiClassKey:

    public <T, Z> DataLoadProvider<T, Z> get(Class<T> dataClass, Class<Z> resourceClass){...}
    
    public class MultiClassKey {
        private Class<?> first;
        private Class<?> second;
        ...
    }
    

      我想表达什么意思呢?

      问题一:如何保证多个对象映射一个value值?

      通常我们的做法是不是有几个对象就写几个映射表,然后都映射一个value值呢?这样做有什么问题呢?
      问题在于增加了代码的编写复杂性,难以阅读和维护,那该如何解决?
      答案是将多个对象封装成一个对象,就是这么easy。

      问题二:get时为什么要通过常量key取值?

      对于源码这种特殊情况,我们通常的做法一般就是遍历HashMap的key,然后通过判断key中每个属性是否和get传参中是否一致来查找指定key(包括各属性值以及组合后的hashCode),但是源码只需要一个GET_KEY便解决的问题。好处在于减小了处理时的时间复杂度,以及更精简的编码,利于维护。

      好了,通过该源码有没有给大家一些启发以及今后的一些开发建议呢?

    相关文章

      网友评论

          本文标题:Glide的DataLoadProviderRegistry类的

          本文链接:https://www.haomeiwen.com/subject/rkdolctx.html