Retrofit 网络框架源码解析

栏目: IOS · Android · 发布时间: 5年前

内容简介:本文介绍 Retrofit 网络框架,包含简单的使用和源码解析。Type-safe HTTP client for Android and Java by Square, Inc.前面介绍过 OkHttp ,Retrofit 是对 OkHttp 网络请求框架的封装,前者专注于接口的封装,后者专注于真正的网络请求。

本文介绍 Retrofit 网络框架,包含简单的使用和源码解析。 本文内容基于 Retrofit 2.4.0 版本

Type-safe HTTP client for Android and Java by Square, Inc. http://square.github.io/retrofit/

前面介绍过 OkHttp ,Retrofit 是对 OkHttp 网络请求框架的封装,前者专注于接口的封装,后者专注于真正的网络请求。

Retrofit 网络框架源码解析

应用程序通过 Retrofit 请求网络,实际上是由 Retrofit 接口层封装请求参数、Header、Url 等信息,由 OkHttp 完成实际的请求操作;在服务端返回数据后,OkHttp 将原始的结果交给 Retrofit,Retrofit 根据用户的需求对结果进行解析。

Retrofit 的简单使用

参考官网的介绍:

1、创建 HTTP API 接口

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

2、创建 Retrofit 实例,并实现接口实例

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
GitHubService service = retrofit.create(GitHubService.class);

3、创建请求实例

Call<List<Repo>> call = service.listRepos("wshunli");

4、发送网络请求

// 同步请求
call.execute();
// 异步请求
call.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {

    }
    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
        Log.d(TAG, "onFailure: ");

    }
});

和 OkHttp 流程差不多,特别是发送请求方法名字都没有变。

Retrofit 的源码分析

Retrofit 网络请求完整的流程图如下:

Retrofit 网络框架源码解析

下面详细介绍。

创建 Retrofit 实例

Retrofit 实例化,也是使用的建造者模式。

Retrofit 网络框架源码解析

我们先看 Builder 成员变量的含义:

// Retrofit#Builder
public static final class Builder {
// 当前系统环境
private final Platform platform;
// 网络请求器的工厂
private @Nullable okhttp3.Call.Factory callFactory;
// 网络请求地址
private HttpUrl baseUrl;
// 数据转换器工厂集合
private final List<Converter.Factory> converterFactories = new ArrayList<>();
// 网络请求适配器工厂集合
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
// 回调方法执行器
private @Nullable Executor callbackExecutor;
// 标志位
private boolean validateEagerly;

1、首先构造函数中通过 Platform.get() 初始化了平台参数

Builder(Platform platform) {
    this.platform = platform;
}
public Builder() {
    this(Platform.get());
}
Builder(Retrofit retrofit) {
    platform = Platform.get();
    callFactory = retrofit.callFactory;
    baseUrl = retrofit.baseUrl;

    converterFactories.addAll(retrofit.converterFactories);
    // Remove the default BuiltInConverters instance added by build().
    converterFactories.remove(0);

    callAdapterFactories.addAll(retrofit.callAdapterFactories);
    // Remove the default, platform-aware call adapter added by build().
    callAdapterFactories.remove(callAdapterFactories.size() - 1);

    callbackExecutor = retrofit.callbackExecutor;
    validateEagerly = retrofit.validateEagerly;
}

我们可以看下判断方法:

// Platform
class Platform {
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

  /* 省略部分无关代码 */

}

后面如果有需要,我们也可以直接拷贝。

2、然后设置 Retrofit 所需的参数即可

public Builder baseUrl(String baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    HttpUrl httpUrl = HttpUrl.parse(baseUrl);
    if (httpUrl == null) {
    throw new IllegalArgumentException("Illegal URL: " + baseUrl);
    }
    return baseUrl(httpUrl);
}
public Builder baseUrl(HttpUrl baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    List<String> pathSegments = baseUrl.pathSegments();
    if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
    throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
    }
    this.baseUrl = baseUrl;
    return this;
}

/** Add converter factory for serialization and deserialization of objects. */
public Builder addConverterFactory(Converter.Factory factory) {
    converterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
}

public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
    callAdapterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
}

3、最后是 build() 方法

public Retrofit build() {
    if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
    }

    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
    callFactory = new OkHttpClient();
    }

    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
    }

    // Make a defensive copy of the adapters and add the default Call adapter.
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

    // Make a defensive copy of the converters.
    List<Converter.Factory> converterFactories =
        new ArrayList<>(1 + this.converterFactories.size());

    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);

    return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
        unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}

创建 API 实例

获取 API 实例使用 Retrofit 的 create() 方法

// Retrofit#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, @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);
            }
        });
}

创建 API 实例使用的是 动态代理 设计模式。

创建请求实例

创建请求实例,跟钱买你的动态代理有关。

// Retrofit#create()
ServiceMethod<Object, Object> serviceMethod =
    (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);

1、 loadServiceMethod() 方法

一个 ServiceMethod 对应于一个 API 接口的一个方法, loadServiceMethod() 方法负责加载 ServiceMethod

// Retrofit#loadServiceMethod()
ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

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

2、 OkHttpCall

OkHttpCall 实现了 retrofit2.Call ,我们通常会使用它的 execute()enqueue() 接口。

OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
}

构造方法也没有什么好看的。

发送网络请求

发送网络请求其实也就是 OkHttpCall 类中的方法。

1、 同步请求 使用 execute() 方法

// OkHttpCall#execute()
@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
        if (executed) throw new IllegalStateException("Already executed.");
        executed = true;

        if (creationFailure != null) {
            if (creationFailure instanceof IOException) {
                throw (IOException) creationFailure;
            } else if (creationFailure instanceof RuntimeException) {
                throw (RuntimeException) creationFailure;
            } else {
                throw (Error) creationFailure;
            }
        }

        call = rawCall;
        if (call == null) {
            try {
                call = rawCall = createRawCall();
            } catch (IOException | RuntimeException | Error e) {
                throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
                creationFailure = e;
                throw e;
            }
        }
    }

    if (canceled) {
        call.cancel();
    }

    return parseResponse(call.execute());
}

这里就是 Retrofit 和 OkHttp 交互的核心了,分为三步:

(1)创建 okhttp3.Call ,包括构造参数

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
        throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}

(2)执行网络请求,也就是 OkHttp 的同步网络请求

call.execute()

(3)解析返回的结果

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
        try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
        } finally {
        rawBody.close();
        }
    }

    if (code == 204 || code == 205) {
        rawBody.close();
        return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
        T body = serviceMethod.toResponse(catchingBody);
        return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        // If the underlying source threw an exception, propagate that rather than indicating it was
        // a runtime exception.
        catchingBody.throwIfCaught();
        throw e;
    }
}

2、 异步请求 使用 enqueue() 方法

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
        if (executed) throw new IllegalStateException("Already executed.");
        executed = true;

        call = rawCall;
        failure = creationFailure;
        if (call == null && failure == null) {
        try {
            call = rawCall = createRawCall();
        } catch (Throwable t) {
            throwIfFatal(t);
            failure = creationFailure = t;
        }
        }
    }

    if (failure != null) {
        callback.onFailure(this, failure);
        return;
    }

    if (canceled) {
        call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
        @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
            response = parseResponse(rawResponse);
        } catch (Throwable e) {
            callFailure(e);
            return;
        }

        try {
            callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
            t.printStackTrace();
        }
        }

        @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
        }

        private void callFailure(Throwable e) {
        try {
            callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
            t.printStackTrace();
        }
        }
    });
}

我们可以看到和同步请求是一致的,实际请求交给了 okhttp3.Call#enqueue(Callback responseCallback) 来实现,并在它的 callback 中调用 parseResponse() 解析响应数据,并转发给传入的 callback

Retrofit 源码就先介绍到这里了,后面有机会再详细介绍。

参考资料

1、Retrofit分析-漂亮的解耦套路 - 简书

https://www.jianshu.com/p/45cb536be2f4

2、Android:手把手带你 深入读懂 Retrofit 2.0 源码 - 简书

https://www.jianshu.com/p/0c055ad46b6c

3、Retrofit源码分析(超详细) - 简书

https://www.jianshu.com/p/097947afddaf

4、拆轮子系列:拆 Retrofit - Piasy的博客 | Piasy Blog

https://blog.piasy.com/2016/06/25/Understand-Retrofit/

5、Retrofit源码解析 | mundane的幻想空间

https://mundane799699.github.io/2018/03/13/retrofit-analysis/

6、Retrofit源码解析 - 掘金

https://juejin.im/post/5acee62c6fb9a028df22ffee

7、Retrofit源码解析 | 俞其荣的博客 | Qirong Yu’s Blog

https://yuqirong.me/2017/08/03/Retrofit源码解析/

8、android-cn/android-open-project-analysis

https://github.com/android-cn/android-open-project-analysis/tree/master/tool-lib/network/retrofit

9、【Android】Retrofit源码分析 - CSDN博客

https://blog.csdn.net/u010983881/article/details/79933220

如果本文对您有所帮助,且您手头还很宽裕,欢迎打赏赞助我,以支付网站服务器和域名费用。 Retrofit 网络框架源码解析 您的鼓励与支持是我更新的最大动力,我会铭记于心,倾于博客。

本文链接: https://www.wshunli.com/posts/2bda06ba.html


以上所述就是小编给大家介绍的《Retrofit 网络框架源码解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

引爆点

引爆点

[美] 马尔科姆·格拉德威尔 / 钱清、覃爱冬 / 中信出版社 / 2009-8 / 27.00元

我们的世界看上去很坚固,但在《纽约客》怪才格拉德威尔的眼里,只要你找到那个点,轻轻一触,这个世界就会动起来:一位满意而归的顾客能让新开张的餐馆座无虚席,一位涂鸦爱好者能在地铁掀起犯罪浪潮,一位精明小伙传递的信息拉开了美国独立战争的序幕——这个看起来不起眼的点,却是任何人都不能忽视的引爆点。 《引爆点》是一本谈论怎样让产品发起流行潮的专门性著作。书中将产品爆发流行的现象归因为三种模式:个别人物......一起来看看 《引爆点》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换