xUtils 源码解析

本文使是以xUtils 2.6的版本来解析源码,可分为以下几个部分:

  • cache 缓存
  • view 注解
  • db 数据库
  • http 网络

1. cache

xUtils 源码解析_第1张图片
屏幕快照 2018-01-28 11.27.34.png
xUtils 源码解析_第2张图片
屏幕快照 2018-01-28 12.01.54.png

首先来看看FileNameGenerator这个接口,其中有一个方法generate方法,生成文件名称,MD5FileNameGenerator实现了该方法,并且采用MD5算法。

public String generate(String key) {
    String cacheKey;
    try {
        final MessageDigest mDigest = MessageDigest.getInstance("MD5");
        mDigest.update(key.getBytes());
        cacheKey = bytesToHexString(mDigest.digest());
    } catch (NoSuchAlgorithmException e) {
        cacheKey = String.valueOf(key.hashCode());
    }
    return cacheKey;
}

private String bytesToHexString(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < bytes.length; i++) {
       String hex = Integer.toHexString(0xFF & bytes[i]);
        if (hex.length() == 1) {
            sb.append('0');
        }
        sb.append(hex);
    }
    return sb.toString();
}

KeyExpiryMap继承自ConcurrentHashMap,存储类型为范型其中Long保存了该key的保存的时间戳。

@Override
public synchronized Long put(K key, Long expiryTimestamp) {
    if (this.containsKey(key)) {
        this.remove(key);
    }
    return super.put(key, expiryTimestamp);
}

@Override
public synchronized boolean containsKey(Object key) {
    boolean result = false;
    Long expiryTimestamp = super.get(key);
    if (expiryTimestamp != null && System.currentTimeMillis() < expiryTimestamp) {
        result = true;
    } else {
    this.remove(key);
    }
    return result;
}

LruMemoryCache 为实现了最近最少使用算法,可参考这边博客Java 最近最少使用算法(LRU)。其内部保存了一个LinkedHashMap、KeyExpiryMap对象的应用。

public final V get(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V mapValue;
    synchronized (this) {
        // If expired, remove the entry.
        if (!keyExpiryMap.containsKey(key)) {
            this.remove(key);
            return null;
        }
        mapValue = map.get(key);
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }

    /*
     * Attempt to create a value. This may take a long time, and the map
     * may be different when create() returns. If a conflicting value was
     * added to the map while create() was working, we leave that value in
     * the map and release the created value.
     */

    V createdValue = create(key);
    if (createdValue == null) {
        return null;
    }

    synchronized (this) {
        createCount++;
        mapValue = map.put(key, createdValue);

        if (mapValue != null) {
            // There was a conflict so undo that last put
            map.put(key, mapValue);
        } else {
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
        trimToSize(maxSize);
        return createdValue;
    }
}

public final V put(K key, V value) {
    return put(key, value, Long.MAX_VALUE);
}

public final V put(K key, V value, long expiryTimestamp) {
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }

    V previous;
    synchronized (this) {
        putCount++;
        size += safeSizeOf(key, value);
        previous = map.put(key, value);
        keyExpiryMap.put(key, expiryTimestamp);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
        entryRemoved(false, key, previous, value);
    }

    trimToSize(maxSize);
    return previous;
}

private void trimToSize(int maxSize) {
    while (true) {
       K key;
       V value;
       synchronized (this) {
            if (size <= maxSize || map.isEmpty()) {
                break;
            }

            Map.Entry toEvict = map.entrySet().iterator().next();
            key = toEvict.getKey();
            value = toEvict.getValue();
            map.remove(key);
            keyExpiryMap.remove(key);
            size -= safeSizeOf(key, value);
            evictionCount++;
       }
       entryRemoved(true, key, value, null);
     }
}

public final V remove(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V previous;
    synchronized (this) {
        previous = map.remove(key);
        keyExpiryMap.remove(key);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
        entryRemoved(false, key, previous, null);
    }
    return previous;
}

2. view

ContentView 、ViewInject、OnClick注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
    int value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
    /* parent view id */
    int parentId() default 0;
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(
        listenerType = View.OnClickListener.class,
        listenerSetter = "setOnClickListener",
        methodName = "onClick")
public @interface OnClick {
    int[] value();
    int[] parentId() default 0;
}

最主要的类就是ViewUtils。

public static void inject(Activity activity) {
    injectObject(activity, new ViewFinder(activity));
}

@SuppressWarnings("ConstantConditions")
private static void injectObject(Object handler, ViewFinder finder) {
    Class handlerType = handler.getClass();
    // inject ContentView
    ContentView contentView = handlerType.getAnnotation(ContentView.class);
    if (contentView != null) {
        try {
            Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
            setContentViewMethod.invoke(handler, contentView.value());
        } catch (Throwable e) {
            LogUtils.e(e.getMessage(), e);
        }
    }

    // inject view
    Field[] fields = handlerType.getDeclaredFields();
    if (fields != null && fields.length > 0) {
        for (Field field : fields) {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                try {
                    View view = finder.findViewById(viewInject.value(), viewInject.parentId());
                    if (view != null) {
                        field.setAccessible(true);
                        field.set(handler, view);
                    }
                } catch (Throwable e) {
                    LogUtils.e(e.getMessage(), e);
                }
            } else {
                ResInject resInject = field.getAnnotation(ResInject.class);
                if (resInject != null) {
                    try {
                        Object res = ResLoader.loadRes(
                                resInject.type(), finder.getContext(), resInject.id());
                        if (res != null) {
                            field.setAccessible(true);
                            field.set(handler, res);
                        }
                    } catch (Throwable e) {
                        LogUtils.e(e.getMessage(), e);
                    }
                } else {
                    PreferenceInject preferenceInject = field.getAnnotation(PreferenceInject.class);
                    if (preferenceInject != null) {
                        try {
                            Preference preference = finder.findPreference(preferenceInject.value());
                            if (preference != null) {
                                field.setAccessible(true);
                                field.set(handler, preference);
                            }
                        } catch (Throwable e) {
                            LogUtils.e(e.getMessage(), e);
                        }
                    }
                }
            }
        }
    }
    // inject event
    Method[] methods = handlerType.getDeclaredMethods();
    if (methods != null && methods.length > 0) {
        for (Method method : methods) {
            Annotation[] annotations = method.getDeclaredAnnotations();
            if (annotations != null && annotations.length > 0) {
                for (Annotation annotation : annotations) {
                    Class annType = annotation.annotationType();
                    if (annType.getAnnotation(EventBase.class) != null) {
                        method.setAccessible(true);
                        try {
                            // ProGuard:-keep class * extends java.lang.annotation.Annotation { *; }
                            Method valueMethod = annType.getDeclaredMethod("value");
                            Method parentIdMethod = null;
                            try {
                                parentIdMethod = annType.getDeclaredMethod("parentId");
                            } catch (Throwable e) {
                            }
                            Object values = valueMethod.invoke(annotation);
                            Object parentIds = parentIdMethod == null ? null : parentIdMethod.invoke(annotation);
                            int parentIdsLen = parentIds == null ? 0 : Array.getLength(parentIds);
                            int len = Array.getLength(values);
                            for (int i = 0; i < len; i++) {
                                ViewInjectInfo info = new ViewInjectInfo();
                                info.value = Array.get(values, i);
                                info.parentId = parentIdsLen > i ? (Integer) Array.get(parentIds, i) : 0;
                               EventListenerManager.addEventMethod(finder, info, annotation, handler, method);
                            }
                        } catch (Throwable e) {
                           LogUtils.e(e.getMessage(), e);
                        }
                    }
                }
            }
        }
    }
}

你可能感兴趣的:(xUtils 源码解析)