[关闭]
@yudesong 2018-02-12T03:13:14.000000Z 字数 6894 阅读 703

xUtils 源码解析(一)

xUtils


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

1. cache

屏幕快照 2018-01-28 11.27.34.png

屏幕快照 2018-01-28 12.01.54.png
首先来看看FileNameGenerator这个接口,其中有一个方法generate方法,生成文件名称,MD5FileNameGenerator实现了该方法,并且采用MD5算法。

  1. public String generate(String key) {
  2. String cacheKey;
  3. try {
  4. final MessageDigest mDigest = MessageDigest.getInstance("MD5");
  5. mDigest.update(key.getBytes());
  6. cacheKey = bytesToHexString(mDigest.digest());
  7. } catch (NoSuchAlgorithmException e) {
  8. cacheKey = String.valueOf(key.hashCode());
  9. }
  10. return cacheKey;
  11. }
  12. private String bytesToHexString(byte[] bytes) {
  13. StringBuilder sb = new StringBuilder();
  14. for (int i = 0; i < bytes.length; i++) {
  15. String hex = Integer.toHexString(0xFF & bytes[i]);
  16. if (hex.length() == 1) {
  17. sb.append('0');
  18. }
  19. sb.append(hex);
  20. }
  21. return sb.toString();
  22. }

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

  1. @Override
  2. public synchronized Long put(K key, Long expiryTimestamp) {
  3. if (this.containsKey(key)) {
  4. this.remove(key);
  5. }
  6. return super.put(key, expiryTimestamp);
  7. }
  8. @Override
  9. public synchronized boolean containsKey(Object key) {
  10. boolean result = false;
  11. Long expiryTimestamp = super.get(key);
  12. if (expiryTimestamp != null && System.currentTimeMillis() < expiryTimestamp) {
  13. result = true;
  14. } else {
  15. this.remove(key);
  16. }
  17. return result;
  18. }

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

  1. public final V get(K key) {
  2. if (key == null) {
  3. throw new NullPointerException("key == null");
  4. }
  5. V mapValue;
  6. synchronized (this) {
  7. // If expired, remove the entry.
  8. if (!keyExpiryMap.containsKey(key)) {
  9. this.remove(key);
  10. return null;
  11. }
  12. mapValue = map.get(key);
  13. if (mapValue != null) {
  14. hitCount++;
  15. return mapValue;
  16. }
  17. missCount++;
  18. }
  19. /*
  20. * Attempt to create a value. This may take a long time, and the map
  21. * may be different when create() returns. If a conflicting value was
  22. * added to the map while create() was working, we leave that value in
  23. * the map and release the created value.
  24. */
  25. V createdValue = create(key);
  26. if (createdValue == null) {
  27. return null;
  28. }
  29. synchronized (this) {
  30. createCount++;
  31. mapValue = map.put(key, createdValue);
  32. if (mapValue != null) {
  33. // There was a conflict so undo that last put
  34. map.put(key, mapValue);
  35. } else {
  36. size += safeSizeOf(key, createdValue);
  37. }
  38. }
  39. if (mapValue != null) {
  40. entryRemoved(false, key, createdValue, mapValue);
  41. return mapValue;
  42. } else {
  43. trimToSize(maxSize);
  44. return createdValue;
  45. }
  46. }
  47. public final V put(K key, V value) {
  48. return put(key, value, Long.MAX_VALUE);
  49. }
  50. public final V put(K key, V value, long expiryTimestamp) {
  51. if (key == null || value == null) {
  52. throw new NullPointerException("key == null || value == null");
  53. }
  54. V previous;
  55. synchronized (this) {
  56. putCount++;
  57. size += safeSizeOf(key, value);
  58. previous = map.put(key, value);
  59. keyExpiryMap.put(key, expiryTimestamp);
  60. if (previous != null) {
  61. size -= safeSizeOf(key, previous);
  62. }
  63. }
  64. if (previous != null) {
  65. entryRemoved(false, key, previous, value);
  66. }
  67. trimToSize(maxSize);
  68. return previous;
  69. }
  70. private void trimToSize(int maxSize) {
  71. while (true) {
  72. K key;
  73. V value;
  74. synchronized (this) {
  75. if (size <= maxSize || map.isEmpty()) {
  76. break;
  77. }
  78. Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
  79. key = toEvict.getKey();
  80. value = toEvict.getValue();
  81. map.remove(key);
  82. keyExpiryMap.remove(key);
  83. size -= safeSizeOf(key, value);
  84. evictionCount++;
  85. }
  86. entryRemoved(true, key, value, null);
  87. }
  88. }
  89. public final V remove(K key) {
  90. if (key == null) {
  91. throw new NullPointerException("key == null");
  92. }
  93. V previous;
  94. synchronized (this) {
  95. previous = map.remove(key);
  96. keyExpiryMap.remove(key);
  97. if (previous != null) {
  98. size -= safeSizeOf(key, previous);
  99. }
  100. }
  101. if (previous != null) {
  102. entryRemoved(false, key, previous, null);
  103. }
  104. return previous;
  105. }

2. view

ContentView 、ViewInject、OnClick注解

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface ContentView {
  4. int value();
  5. }
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface ViewInject {
  9. int value();
  10. /* parent view id */
  11. int parentId() default 0;
  12. }
  13. @Target(ElementType.METHOD)
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @EventBase(
  16. listenerType = View.OnClickListener.class,
  17. listenerSetter = "setOnClickListener",
  18. methodName = "onClick")
  19. public @interface OnClick {
  20. int[] value();
  21. int[] parentId() default 0;
  22. }

最主要的类就是ViewUtils。

  1. public static void inject(Activity activity) {
  2. injectObject(activity, new ViewFinder(activity));
  3. }
  4. @SuppressWarnings("ConstantConditions")
  5. private static void injectObject(Object handler, ViewFinder finder) {
  6. Class<?> handlerType = handler.getClass();
  7. // inject ContentView
  8. ContentView contentView = handlerType.getAnnotation(ContentView.class);
  9. if (contentView != null) {
  10. try {
  11. Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
  12. setContentViewMethod.invoke(handler, contentView.value());
  13. } catch (Throwable e) {
  14. LogUtils.e(e.getMessage(), e);
  15. }
  16. }
  17. // inject view
  18. Field[] fields = handlerType.getDeclaredFields();
  19. if (fields != null && fields.length > 0) {
  20. for (Field field : fields) {
  21. ViewInject viewInject = field.getAnnotation(ViewInject.class);
  22. if (viewInject != null) {
  23. try {
  24. View view = finder.findViewById(viewInject.value(), viewInject.parentId());
  25. if (view != null) {
  26. field.setAccessible(true);
  27. field.set(handler, view);
  28. }
  29. } catch (Throwable e) {
  30. LogUtils.e(e.getMessage(), e);
  31. }
  32. } else {
  33. ResInject resInject = field.getAnnotation(ResInject.class);
  34. if (resInject != null) {
  35. try {
  36. Object res = ResLoader.loadRes(
  37. resInject.type(), finder.getContext(), resInject.id());
  38. if (res != null) {
  39. field.setAccessible(true);
  40. field.set(handler, res);
  41. }
  42. } catch (Throwable e) {
  43. LogUtils.e(e.getMessage(), e);
  44. }
  45. } else {
  46. PreferenceInject preferenceInject = field.getAnnotation(PreferenceInject.class);
  47. if (preferenceInject != null) {
  48. try {
  49. Preference preference = finder.findPreference(preferenceInject.value());
  50. if (preference != null) {
  51. field.setAccessible(true);
  52. field.set(handler, preference);
  53. }
  54. } catch (Throwable e) {
  55. LogUtils.e(e.getMessage(), e);
  56. }
  57. }
  58. }
  59. }
  60. }
  61. }
  62. // inject event
  63. Method[] methods = handlerType.getDeclaredMethods();
  64. if (methods != null && methods.length > 0) {
  65. for (Method method : methods) {
  66. Annotation[] annotations = method.getDeclaredAnnotations();
  67. if (annotations != null && annotations.length > 0) {
  68. for (Annotation annotation : annotations) {
  69. Class<?> annType = annotation.annotationType();
  70. if (annType.getAnnotation(EventBase.class) != null) {
  71. method.setAccessible(true);
  72. try {
  73. // ProGuard:-keep class * extends java.lang.annotation.Annotation { *; }
  74. Method valueMethod = annType.getDeclaredMethod("value");
  75. Method parentIdMethod = null;
  76. try {
  77. parentIdMethod = annType.getDeclaredMethod("parentId");
  78. } catch (Throwable e) {
  79. }
  80. Object values = valueMethod.invoke(annotation);
  81. Object parentIds = parentIdMethod == null ? null : parentIdMethod.invoke(annotation);
  82. int parentIdsLen = parentIds == null ? 0 : Array.getLength(parentIds);
  83. int len = Array.getLength(values);
  84. for (int i = 0; i < len; i++) {
  85. ViewInjectInfo info = new ViewInjectInfo();
  86. info.value = Array.get(values, i);
  87. info.parentId = parentIdsLen > i ? (Integer) Array.get(parentIds, i) : 0;
  88. EventListenerManager.addEventMethod(finder, info, annotation, handler, method);
  89. }
  90. } catch (Throwable e) {
  91. LogUtils.e(e.getMessage(), e);
  92. }
  93. }
  94. }
  95. }
  96. }
  97. }
  98. }

其中ViewFinder类如下:
```
public class ViewFinder {

private View view;
private Activity activity;
public ViewFinder(View view) {
    this.view = view;
}

public ViewFinder(Activity activity) {
    this.activity = activity;
}

......
public View findViewById(int id, int pid) {
    View pView = null;
    if (pid > 0) {
        pView = this.findViewById(pid);
    }

    View view = null;
    if (pView != null) {
        view = pView.findViewById(id);
    } else {
        view = this.findViewById(id);
    }
    return view;
}

@SuppressWarnings("deprecation")
public Preference findPreference(CharSequence key) {
    return preferenceGroup == null ? preferenceActivity.findPreference(key) : preferenceGroup.findPreference(key);
}

}
```

参考文章
1.trimToSize 用法

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注