上一章已经介绍了Retrofit创建过程,这章介绍Retrofit Api Service创建与访问过程。

Retrofit 相比Volley等网络框架一个最大区别就是它只需要声明接口,就可以访问网络,刚刚使用的时候觉得很神奇,有木有。

要彻底理解底层实现访问,需要先来看看Java的动态代理。

public interface Subject {boolean login(String username, String password);
}
/**
 * 真实对象
 * @author Micky Liu
 *
 */
public class RealSubject implements Subject {@Override
    public boolean login(String username, String password) {System.out.println("username = " + username + ", passwd = " + password);
        return "MickyLiu".equals(username) && "123456".equals(password);
    }
}
/**
 * 动态代理类
 * @author Micky Liu
 *
 */
public class DynamicProxy implements InvocationHandler {/**需要代理的真实对象*/
    private Object subject;

    public DynamicProxy(Object subject) {this.subject = subject;
    }@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoke subject method \"" + method + "\"");
        boolean b = (boolean) method.invoke(subject, args);
        System.out.println("After invoked subject method \"" + method + "\"") ;
        return b;
    }
}
public class ProxySample {public static void main(String[] args) {//真实对象
        Subject realSubject = new RealSubject();

        //代理对象
        InvocationHandler handler  = new DynamicProxy(realSubject);

        //Proxy.newProxyInstance有三个参数
        //参数1:代理类加载器
        //参数2:真实对象实现的接口,这里用于关联代理对象和真实对象,确保代理对象里调用的方法是和真实对象同名的方法
        //参数3:代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
        boolean isSuccess = subject.login("MickyLiu", "123456");
        String result = isSuccess ? "login success" : "login failed";
        System.out.println(result);
    }
}

Retrofit除了使用上述的动态代理,还使用了Annotation即注解。在Retrofit中使用了自定义注解,如@POST @GET @Path @Query等,就像我们在接口中定义的:

@POST(Constants.URL_UPDATE_USER_LIST)
Observable<BaseResponse<List<User>>> updateUserList(@Query("userList") String userList);

看看我们前面使用过的代码:

Retrofit retrofit = new Retrofit.Builder().baseUrl(getEndPoint(t)).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).client(clientBuilder.build()).build();
service = retrofit.create(t);

这章的重点 retrofit.create方法,它用来创建API Service接口,返回的对象是我们定义的Service,有了它我们就能直接调用service.updateUserList来访问网络接口了。来查看下create的内部实现

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

        @Override public Object invoke(Object proxy, Method method, 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);
          }if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);
          }ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }});
}

首先调用validateServiceInterface只是调用了service.isInterface()方法判断是否这个对象是一个接口对象,不要问我isInterface()方法哪里来的,去jdk的Class类里找。

往下是不是看见了前面介绍的动态代理?好吧,我明白了,Retrofit 不就是简单的使用动态代理,就实现了所谓只要声明接口就能访问网络了么。简单么?

到这里我们已经知道retrofit 内部是怎么创建的了,接下来就看看它是怎么完成访问流程的。

  method.getDeclaringClass() == Object.class //这段代码用来判断当前方法是否是声明在Object中的访问
  platform.isDefaultMethod(method) //这段代码用来判断是否是默认代码,当Platform是Android的时候永远返回是false

接下来的代码才是我们的重点了

ServiceMethod loadServiceMethod(Method method) {ServiceMethod result;
  synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);
    if (result == null) {result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }}return result;
}

看看ServiceMethodCache的定义,键:method对象, 值ServiceMethod对象,如果已经Map中缓存了这个method则直接取,没有则创建一个ServiceMethod存到map中并将这个serviceMethod返回。

private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();

ServiceMethod.Builder类:

public Builder(Retrofit retrofit, Method method) {this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

methodAnnotations是方法注解即POST(url),实际取到的值:@retrofit2.http.POST(value=UpdateUserList)]

parameterTypes 是参数类型的数组,实际取到的值:[class java.lang.String]

parameterAnnotationsArray 是参数注解即@Query(“userList”),实际取到的值:[@retrofit2.http.Query(encoded=false, value=userList)]

ServiceMethod.Builder.build()方法:

public ServiceMethod build() {callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {throw methodError("'"
        + Utils.getRawType(responseType).getName()+ "' is not a valid response body type. Did you mean ResponseBody?");
  }responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);
  }if (httpMethod == null) {throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }if (!hasBody) {if (isMultipart) {throw methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }if (isFormEncoded) {throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST).");
    }}int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {throw parameterError(p, "No Retrofit annotation found.");
    }parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }if (relativeUrl == null && !gotUrl) {throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body.");
  }if (isFormEncoded && !gotField) {throw methodError("Form-encoded method must contain at least one @Field.");
  }if (isMultipart && !gotPart) {throw methodError("Multipart method must contain at least one @Part.");
  }return new ServiceMethod<>(this);
}

返回的callAdapter为我们配置的“class retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$SimpleCallAdapter”

responseConverter为我们配置的“class retrofit2.converter.gson.GsonResponseBodyConverter”

parseMethodAnnotation用于将我们接口的method注解解析出来,保存在ServiceMethod.Builder类的成员变量里,以供后续使用。

接着的代码是通过获取参数注解解析我们的参数列表。

private CallAdapter<?> createCallAdapter() {Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {throw methodError("Method return type must not include a type variable or wildcard: %s", returnType);
  }if (returnType == void.class) {throw methodError("Service methods cannot return void.");
  }Annotation[] annotations = method.getAnnotations();
  try {return retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    throw methodError(e, "Unable to create call adapter for %s", returnType);
  }
}

此处可以获取到returnType为rx.Observable<com.micky.retrofitsample.netservice.response.BaseResponse<java.util.List<com.micky.retrofitsample.model.User>>>,这不就是我们定义的接口返回类型么。

Retrofit详解(二)(Retrofit核心流程)相关推荐

  1. Retrofit详解(一)(Retrofit创建过程)

    作为一个coder 最悲哀的莫过于知其然,不知其所以然. 闲暇之余,研究研究Retrofit源码,为了防止大篇幅的代码,看得头晕眼花,这章仅仅详细介绍Retrofit 的创建过程.Retrofit使用 ...

  2. JAVAWEB开发之工作流详解(二)——Activiti核心API的使用(流程定义和流程实例的管理、流程变量、监听器...)以及与Spring的集成

    管理流程定义 设计流程定义文档 bpmn文件 设置方式可以直接使用插件图形化界面进行设置 为某任务节点指定任务执行者 保存后的BPMN文件可以使用XML编辑器打开 BPMN 2.0根节点是defini ...

  3. EXT核心API详解(二)-Array/Date/Function/Number/String

    EXT核心API详解(二)-Array/Date/Function/Number/String Array类 indexOf( Object o )  Number object是否在数组中,找不到返 ...

  4. PackageManagerService启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理?

    PKMS启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理? Android PackageManagerService系列博客目录: PKMS启动详解系列博客概要 PKM ...

  5. Pytorch|YOWO原理及代码详解(二)

    Pytorch|YOWO原理及代码详解(二) 本博客上接,Pytorch|YOWO原理及代码详解(一),阅前可看. 1.正式训练 if opt.evaluate:logging('evaluating ...

  6. Android面试Hash原理详解二

    Hash系列目录 Android面试Hash原理详解一 Android面试Hash原理详解二 Android面试Hash常见算法 Android面试Hash算法案例 Android面试Hash原理详解 ...

  7. 全志 android 编译,全志Android SDK编译详解(二)

    注意要确定安装了jdk) 第一步: cd  lichee; ./build.sh  -p sun5i_elite -k 3.0  (apt-get install uboot-mkimage需要安装m ...

  8. 计算机考试老师怎么评卷,详解高考电脑阅卷流程,原来老师看到的试卷是这样的!...

    原标题:详解高考电脑阅卷流程,原来老师看到的试卷是这样的! 距离2017年高考越来越近了,考生和家长都知道高考阅卷是采用电脑阅卷的模式,但是电脑如何阅卷?老师如何阅卷?哪些行为会造成答题不规范?怎样答 ...

  9. 红黑树详解(二)红黑树的插入(附动图和案例)

    红黑树详解(二)红黑树的插入(附动图和案例) 摘要: 在很多源码涉及到大量数据处理的时候,通常都是用红黑树这一数据结构.红黑树是一种自平衡的二叉查找树,它能在进行插入和删除操作时通过特定操作保持二叉查 ...

最新文章

  1. 设置Enter键为默认键
  2. LeetCode-剑指 Offer 06. 从尾到头打印链表
  3. 认识因特网络(小学计算机课件),小学信息技术认识因特网ppt课件.ppt
  4. 前端学习(2518):生命周期钩子
  5. 初始化java工具失败,“初始化 Java 工具”期间发生了内部错误, java.lang.NullPointerException...
  6. python selenium canvas_selenium webdriver 实现Canvas画布自动化测试
  7. docker环境搭建redis-cluster集群(多台机器)
  8. 系统的延时与定时任务
  9. DOTA 2血虐人类的OpenAI,原来靠的是作弊?
  10. Vue2.0实现1.0的搜索过滤器功能
  11. android动态加载.so,实现动态库升级
  12. application context not configured for this file于spring框架使用中的原因
  13. Atitit  循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate).
  14. 【Bugs】Hbase:File system needs to be upgraded. You have version null and I want version(habse缓冲问题)
  15. Hex Fiend – 十六进制编辑 [Mac]
  16. 高斯分布估计子的性能与克拉默劳下界的讨论
  17. 详解用爬虫批量抓取猫眼电影票房数据
  18. Salesforce Apex 触发器学习记录
  19. 2020年自考计算机应用基础和实践是什么,2020年自考计算机应用基础复习重点8
  20. java-网页404(个例)

热门文章

  1. 宇视摄像机——枪机后焦调节方法
  2. 这些成为网络工程师的基本技能要求,你拥有哪些?
  3. JAVA生成二维码(二)深度处理
  4. 今天特雷西·麦克格雷迪31岁生日
  5. Qt绘图:求圆和椭圆上任意角度点的坐标
  6. adb 前摄像头 调用_android: 调用摄像头拍照
  7. CHINAPLAS国际橡塑展落户深圳,扬帆启航踏新程
  8. 更改极点为第一输入法
  9. flash builder (fb) 与flash professional cs6(fla) 联合调试
  10. 王道出版的机试指南_《王道论坛计算机考研机试指南》试读版.pdf