继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

retrofit介绍

nickcau
关注TA
已关注
手记 114
粉丝 6509
获赞 303

Retrofit是什么

简单的说它是一个基于OkHttp的RESTFUL Api请求工具,从功能上来说和Google的Volley功能上很相似,但是使用上很不相似。

Retrofit可以让你简单到调用一个Java方法的方式去请求一个api,这样App中的代码就会很简洁方便阅读

retrofit的好处:

① Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;

② Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;

④ Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;

④ Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。

 Retrofit怎么用

比如你要请求这么一个api,

http://139.199.89.89/api/v1/books

首先,你需要创建一个Retrofit对象,并且指定api的域名:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://139.199.89.89") //设置网络请求的Url地址
        .addConverterFactory(GsonConverterFactory.create()) //设置数据解析器
        .build();

其次,你要根据api新建一个Java接口,用Java注解来描述这个api

public interface BookService {
    @GET("/api/v1/books")
    Call<BookResponse> getResult();
}

再用这个retrofit对象创建一个BookService对象:

Call<BookResponse> call = service.getResult();

这样就表示你要请求的api是http://139.199.89.89/api/v1/books

最后你就可以用这个call对象获得数据了,enqueue方法是异步发送http请求的,如果你想用同步的方式发送可以使用execute()方法,call对象还提供cancel()isCancel()等方法获取这个Http请求的状态

call.enqueue(new Callback<BookResponse>() {
    @Override
    public void onResponse(Call<BookResponse> call, Response<BookResponse> response) {
        Log.d(TAG,"<<<<<response="+response);
    }

    @Override
    public void onFailure(Call<BookResponse> call, Throwable t) {

    }
});

Retrofit只要创建一个接口来描述Http请求,然后可以让我们可以像调用Java方法一样请求一个Api

Retrofit的原理

从上面Retrofit的使用来看,Retrofit就是充当了一个适配器(Adapter)的角色:将一个Java接口翻译成一个Http请求,然后用OkHttp去发送这个请求**

Volley描述一个HTTP请求是需要创建一个Request对象,而执行这个请求呢,就是把这个请求对象放到一个队列中,在网络线程中用HttpUrlConnection去请求

问题来了:

Retrofit是怎么做的呢?

答案很简单,就是:Java的动态代理

动态代理

Call<BookResponse> call = service.getResult();

我给Retrofit对象传了一个BookService接口的Class对象,怎么又返回一个BookService对象呢?进入create方法一看,没几行代码,但是我觉得这几行代码就是Retrofit的精妙的地方:

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

create方法就是返回了一个Proxy.newProxyInstance动态代理对象。那么问题来了...

动态代理是个什么东西?

看Retrofit代码之前我知道Java动态代理是一个很重要的东西,比如在Spring框架里大量的用到,但是它有什么用呢?

Java动态代理就是给了程序员一种可能:当你要调用某个Class的方法前或后,插入你想要执行的代码

比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱。这么简单的一句话,我相信可以把一个不懂技术的人也讲明白Java动态代理是什么东西了。

为什么要使用动态代理

你看上面代码,获取数据的代码就是这句:

Call<BookResponse> call = service.getResult();

上面api对象其实是一个动态代理对象,并不是一个真正的BookService接口的implements产生的对象,当api对象调用getResult方法时会被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象,它的invoke方法会传入3个参数:

  • Object proxy: 代理对象,不关心这个

  • Method method:调用的方法,就是getAuthor方法

  • Object... args:方法的参数,就是"qinchao"

而Retrofit关心的就是method和它的参数args,接下去Retrofit就会用Java反射获取到getResult方法的注解信息,配合args参数,创建一个ServiceMethod对象

ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client

使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送

执行Http请求

之前讲到,OkHttpCall是实现了Call接口的,并且是真正调用OkHttp3发送Http请求的类。OkHttp3发送一个Http请求需要一个Request对象,而这个Request对象就是从ServiceMethodtoRequest返回的

总的来说,OkHttpCall就是调用ServiceMethod获得一个可以执行的Request对象,然后等到Http请求返回后,再将response body传入ServiceMethod中,ServiceMethod就可以调用Converter接口将response body转成一个Java对象

结合上面说的就可以看出,ServiceMethod中几乎保存了一个api请求所有需要的数据,OkHttpCall需要从ServiceMethod中获得一个Request对象,然后得到response后,还需要传入ServiceMethodConverter转换成Java对象

你可能会觉得我只要发送一个HTTP请求,你要做这么多事情不会很“慢”吗?不会很浪费性能吗?

我觉得,首先现在手机处理器主频非常高了,解析这个接口可能就花1ms可能更少的时间(我没有测试过),面对一个HTTP本来就需要几百ms,甚至几千ms来说不值得一提;而且Retrofit会对解析过的请求进行缓存,就在Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();这个对象中


Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求

Retrofit的功能非常多的依赖Java反射,代码中其实还有很多细节,比如异常的捕获、抛出和处理,大量的Factory设计模式(为什么要这么多使用Factory模式?)

Retrofit中接口设计的恰到好处,在你创建Retrofit对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能

我也慢慢看了Picasso和Retrofit的代码了,收获还是很多的,也更加深入的理解面向接口的编程方法,这个写代码就是好的代码就是依赖接口而不是实现最好的例子

好感谢开源的世界,让我能读到大牛的代码。我一直觉得一个人如果没有读过好的代码是不太可能写出好代码的。什么是好的代码?像Picasso和Retrofit这样的就是好的代码,扩展性强、低耦合、插件化



打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP