美文网首页
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