retrofit 源码优秀代码功能点学习

@Nullable和@Nonnull的使用

在编写方法时,入参、返回值,记得要用@Nullable和@Nonnull修饰,来达到提醒调用方的目的(kotlin应该不用)

泛型的大量使用

一个好的架构,一定是可以兼容各种数据类型,各种场景的,所以通过泛型可以很好的解决这些问题,所以泛型的使用是架构设计的核心

public interface TypeQualifierValidator<A extends Annotation> {
    public @Nonnull
    When forConstantValue(@Nonnull A annotation, Object value);
}
public final class Response<T> {
  /** Create a synthetic successful response with {@code body} as the deserialized body. */
  public static <T> Response<T> success(@Nullable T body) {
    return success(
        body,
        new okhttp3.Response.Builder() //
            .code(200)
            .message("OK")
            .protocol(Protocol.HTTP_1_1)
            .request(new Request.Builder().url("http://localhost/").build())
            .build());
  }
}  
public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
}

private void validateServiceInterface(Class<?> service) {
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    
    Deque<Class<?>> check = new ArrayDeque<>(1);
}

final class

retrofit 源码中有大量的类都是使用final来修饰的
final修饰class有以下特点 :

  • 当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
  • 所以说,当开发时,如果写的类是非Activity、Fragment、View、Service等非android类时,应尽量考虑final class的情况

String类就是一个final类:

public final class String implements Serializable, Comparable<String>, CharSequence {
    ...
}

final 修饰方法时:

  • 即父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的
  • 不能被重写
public class B extends A {
  public void getName() {

  }
}

class A {
  public final void getName() {

  }
}

当A类的getName是被final修饰时,如果B extends A ,那么不能重写getName方法,会直接报错:
getName()' cannot override 'getName()' in 'retrofit2.A'; overridden method is final

final 修饰变量时:

  • final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

static final class

public static final class Builder {}

build模式和工厂模式(工厂、抽象工厂)的使用

<? extends Annotation>和<?, ?>的使用

@Override
public Class<? extends Annotation> annotationType() {
    return SkipCallbackExecutor.class;
}

abstract class(抽象类)的使用

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(
          method,
          "Method return type must not include a type variable or wildcard: %s",
          returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

使用String字符串拼接时,要用StringBuilder

StringBuilder builder =
    new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n");
if (skipPast != null) {
  builder.append("  Skipped:");
  for (int i = 0; i < start; i++) {
    builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
  }
  builder.append('\n');
}
builder.append("  Tried:");
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
  builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());

StringBuilder效率高,线程不安全
StringBuffer效率低,线程安全

Objects.requireNonNull(type, "type == null");的使用

Objects.requireNonNull(type, "type == null");
Objects.requireNonNull(parameterAnnotations, "parameterAnnotations == null");
Objects.requireNonNull(methodAnnotations, "methodAnnotations == null");

底层(sdk组件)字段校验可以使用这样的方式,来避免调用方传一些错误字段,但是上层业务不能这样写,要处理异常而不是抛出

for循环中创建count对象

for (int i = start, count = converterFactories.size(); i < count; i++) {
  Converter.Factory factory = converterFactories.get(i);
  Converter<?, RequestBody> converter =
      factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
  if (converter != null) {
    //noinspection unchecked
    return (Converter<T, RequestBody>) converter;
  }
}

for循环中,使用count = converterFactories.size()的方法,直接创建了count对象

反射reflect中Type的意义和使用

/**
 * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
 * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
 */
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
  return Utils.getParameterUpperBound(index, type);
}

/**
 * Extract the raw class type from {@code type}. For example, the type representing {@code
 * List<? extends Runnable>} returns {@code List.class}.
 */
protected static Class<?> getRawType(Type type) {
  return Utils.getRawType(type);
}

List作为返回值,而不是ArrayList等

public List<Converter.Factory> converterFactories() {
return converterFactories;
}

动态代理的使用

public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

用线程池Executor而不是直接使用new Thread()

final @Nullable Executor callbackExecutor;

package-info.java的使用

@retrofit2.internal.EverythingIsNonNull
package retrofit2;

package-info.java是一个特殊的类,可以对整个包做操作。
retrofit中使用package-info.java为retrofit2包中的每一个类都添加了注释@retrofit2.internal.EverythingIsNonNull

kotlin中泛型、内联函数、扩展函数的用法:


inline fun <reified T> Retrofit.create(): T = create(T::class.java)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容