/*
* Copyright (C) 2012 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package retrofit2;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.Header;
import retrofit2.http.Url;
import static java.util.Collections.unmodifiableList;
import static retrofit2.Utils.checkNotNull;
/**
* Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to
* define how requests are made. Create instances using {@linkplain Builder
* the builder} and pass your interface to {@link #create} to generate an implementation.
* <p>
* For example,
* <pre><code>
* Retrofit retrofit = new Retrofit.Builder()
* .baseUrl("https://api.example.com/")
* .addConverterFactory(GsonConverterFactory.create())
* .build();
*
* MyApi api = retrofit.create(MyApi.class);
* Response<User> user = api.getUser().execute();
* </code></pre>
*
* @author Bob Lee ([email protected])
* @author Jake Wharton ([email protected])
*/
public final class Retrofit {
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
private final okhttp3.Call.Factory callFactory;
private final HttpUrl baseUrl;//url地址 这个在平时工作中是必须要设置的,必不可少
private final List<Converter.Factory> converterFactories;//转换工厂?
private final List<CallAdapter.Factory> adapterFactories;//适配工厂
private final Executor callbackExecutor;
private final boolean validateEagerly;
//带参数构造器
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
/**
* Create an implementation of the API endpoints defined by the {@code service} interface.
* <p>
* The relative path for a given method is obtained from an annotation on the method describing
* the request type. The built-in methods are {@link GET GET},
* {@link retrofit2.http.PUT PUT}, {@link retrofit2.http.POST POST}, {@link retrofit2.http.PATCH
* PATCH}, {@link retrofit2.http.HEAD HEAD}, {@link retrofit2.http.DELETE DELETE} and
* {@link retrofit2.http.OPTIONS OPTIONS}. You can use a custom HTTP method with
* {@link HTTP @HTTP}. For a dynamic URL, omit the path on the annotation and annotate the first
* parameter with {@link Url @Url}.
* <p>
* Method parameters can be used to replace parts of the URL by annotating them with
* {@link retrofit2.http.Path @Path}. Replacement sections are denoted by an identifier
* surrounded by curly braces (e.g., "{foo}"). To add items to the query string of a URL use
* {@link retrofit2.http.Query @Query}.
* <p>
* The body of a request is denoted by the {@link retrofit2.http.Body @Body} annotation. The
* object will be converted to request representation by one of the {@link Converter.Factory}
* instances. A {@link RequestBody} can also be used for a raw representation.
* <p>
* Alternative request body formats are supported by method annotations and corresponding
* parameter annotations:
* <ul>
* <li>{@link retrofit2.http.FormUrlEncoded @FormUrlEncoded} - Form-encoded data with key-value
* pairs specified by the {@link retrofit2.http.Field @Field} parameter annotation.
* <li>{@link retrofit2.http.Multipart @Multipart} - RFC 2388-compliant multipart data with
* parts specified by the {@link retrofit2.http.Part @Part} parameter annotation.
* </ul>
* <p>
* Additional static headers can be added for an endpoint using the
* {@link retrofit2.http.Headers @Headers} method annotation. For per-request control over a
* header annotate a parameter with {@link Header @Header}.
* <p>
* By default, methods return a {@link Call} which represents the HTTP request. The generic
* parameter of the call is the response body type and will be converted by one of the
* {@link Converter.Factory} instances. {@link ResponseBody} can also be used for a raw
* representation. {@link Void} can be used if you do not care about the body contents.
* <p>
* For example:
* <pre>
* public interface CategoryService {
* @POST("category/{cat}/")
* Call<List<Item>> categoryList(@Path("cat") String a, @Query("page") int b);
* }
* </pre>
*/
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
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);
}
});
}
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
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;
}
/**
* The factory used to create {@linkplain okhttp3.Call OkHttp calls} for sending a HTTP requests.
* Typically an instance of {@link OkHttpClient}.
*/
public okhttp3.Call.Factory callFactory() {
return callFactory;
}
/** The API base URL. */
//返回服务器地址
public HttpUrl baseUrl() {
return baseUrl;
}
/**
* Returns a list of the factories tried when creating a
* {@linkplain #callAdapter(Type, Annotation[])} call adapter}.
*/
public List<CallAdapter.Factory> callAdapterFactories() {
return adapterFactories;
}
/**
* Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
* #callAdapterFactories() factories}.
*
* @throws IllegalArgumentException if no call adapter available for {@code type}.
*/
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
/**
* Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
* #callAdapterFactories() factories} except {@code skipPast}.
*
* @throws IllegalArgumentException if no call adapter available for {@code type}.
*/
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
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(adapterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = adapterFactories.size(); i < count; i++) {
builder.append("\n * ").append(adapterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
/**
* Returns a list of the factories tried when creating a
* {@linkplain #requestBodyConverter(Type, Annotation[], Annotation[]) request body converter}, a
* {@linkplain #responseBodyConverter(Type, Annotation[]) response body converter}, or a
* {@linkplain #stringConverter(Type, Annotation[]) string converter}.
*/
//返回转换工厂列表?
public List<Converter.Factory> converterFactories() {
return converterFactories;
}
/**
* Returns a {@link Converter} for {@code type} to {@link RequestBody} from the available
* {@linkplain #converterFactories() factories}.
*
* @throws IllegalArgumentException if no converter available for {@code type}.
*/
public <T> Converter<T, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}
/**
* Returns a {@link Converter} for {@code type} to {@link RequestBody} from the available
* {@linkplain #converterFactories() factories} except {@code skipPast}.
*
* @throws IllegalArgumentException if no converter available for {@code type}.
*/
public <T> Converter<T, RequestBody> nextRequestBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
checkNotNull(type, "type == null");//非空判断
checkNotNull(parameterAnnotations, "parameterAnnotations == null");
checkNotNull(methodAnnotations, "methodAnnotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
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;
}
}
StringBuilder builder = new StringBuilder("Could not locate RequestBody converter for ")
.append(type)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
/**
* Returns a {@link Converter} for {@link ResponseBody} to {@code type} from the available
* {@linkplain #converterFactories() factories}.
*
* @throws IllegalArgumentException if no converter available for {@code type}.
*/
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
/**
* Returns a {@link Converter} for {@link ResponseBody} to {@code type} from the available
* {@linkplain #converterFactories() factories} except {@code skipPast}.
*
* @throws IllegalArgumentException if no converter available for {@code type}.
*/
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");//非空判断
checkNotNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
.append(type)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
/**
* Returns a {@link Converter} for {@code type} to {@link String} from the available
* {@linkplain #converterFactories() factories}.
*/
public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");//非空判断
checkNotNull(annotations, "annotations == null");
for (int i = 0, count = converterFactories.size(); i < count; i++) {
Converter<?, String> converter =
converterFactories.get(i).stringConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<T, String>) converter;
}
}
// Nothing matched. Resort to default converter which just calls toString().
//noinspection unchecked
return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}
/**
* The executor used for {@link Callback} methods on a {@link Call}. This may be {@code null},
* in which case callbacks should be made synchronously on the background thread.
*/
public Executor callbackExecutor() {
return callbackExecutor;
}
/**
//这里又是构建者模式的运用?
* Build a new {@link Retrofit}.
* <p>
* Calling {@link #baseUrl} is required before calling {@link #build()}. All other methods
* are optional.
*/
public static final class Builder {
private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
// 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());
}
public Builder() {
this(Platform.get());
}
/**
* The HTTP client used for requests.
* <p>
* This is a convenience method for calling {@link #callFactory}.
* <p>
* Note: This method <b>does not</b> make a defensive copy of {@code client}. Changes to its
* settings will affect subsequent requests. Pass in a {@linkplain OkHttpClient#clone() cloned}
* instance to prevent this if desired.
*/
////设置使用OkHttp网络请求,okhttp是高性能的http库,拥有队列线程池,轻松解决并发,自动维护socket连接池
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
/**
* Specify a custom call factory for creating {@link Call} instances.
* <p>
* Note: Calling {@link #client} automatically sets this value.
*/
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
/**
* Set the API base URL.
*
* @see #baseUrl(HttpUrl)
*/
//设置服务器访问地址
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);
}
/**
* Set the API base URL.
* <p>
* The specified endpoint values (such as with {@link GET @GET}) are resolved against this
* value using {@link HttpUrl#resolve(String)}. The behavior of this matches that of an
* {@code <a href="">} link on a website resolving on the current URL.
* <p>
* <b>Base URLs should always end in {@code /}.</b>
* <p>
* A trailing {@code /} ensures that endpoints values which are relative paths will correctly
* append themselves to a base which has path components.
* <p>
* <b>Correct:正确的服务器地址格式如下</b><br>
* Base URL: http://example.com/api/<br>
* Endpoint: foo/bar/<br>
* Result: http://example.com/api/foo/bar/
* <p>
* <b>Incorrect:错误的服务器地址格式如下</b><br>
* Base URL: http://example.com/api<br>
* Endpoint: foo/bar/<br>
* Result: http://example.com/foo/bar/
* <p>
* This method enforces that {@code baseUrl} has a trailing {@code /}.
* <p>
* <b>Endpoint values which contain a leading {@code /} are absolute.</b>
* <p>
* Absolute values retain only the host from {@code baseUrl} and ignore any specified path
* components.
* <p>
* Base URL: http://example.com/api/<br>
* Endpoint: /foo/bar/<br>
* Result: http://example.com/foo/bar/
* <p>
* Base URL: http://example.com/<br>
* Endpoint: /foo/bar/<br>
* Result: http://example.com/foo/bar/
* <p>
* <b>Endpoint values may be a full URL.</b>
* <p>
* Values which have a host replace the host of {@code baseUrl} and values also with a scheme
* replace the scheme of {@code baseUrl}.
* <p>
* Base URL: http://example.com/<br>
* Endpoint: https://github.com/square/retrofit/<br>
* Result: https://github.com/square/retrofit/
* <p>
* Base URL: http://example.com<br>
* Endpoint: //github.com/square/retrofit/<br>
* Result: http://github.com/square/retrofit/ (note the scheme stays 'http')
*/
//设置服务器访问地址
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. */
////添加转化库,默认是Gson? 例如:GsonConverterFactory.create()
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/**
* Add a call adapter factory for supporting service method return types other than {@link
* Call}.
*/
////添加回调库,可以采用RxJava 例如:RxJavaCallAdapterFactory.create()
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
adapterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
/**
* The executor on which {@link Callback} methods are invoked when returning {@link Call} from
* your service method.
* <p>
* Note: {@code executor} is not used for {@linkplain #addCallAdapterFactory custom method
* return types}.
*/
public Builder callbackExecutor(Executor executor) {
this.callbackExecutor = checkNotNull(executor, "executor == null");
return this;
}
/**
* When calling {@link #create} on the resulting {@link Retrofit} instance, eagerly validate
* the configuration of all methods in the supplied interface.
*/
public Builder validateEagerly(boolean validateEagerly) {
this.validateEagerly = validateEagerly;
return this;
}
/**
* Create the {@link Retrofit} instance using the configured values.
* <p>
* Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
* OkHttpClient} will be created and used.
*/
//最后一步,建立实例
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> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);//返回结果
}
}//构建者类结束
}//game over
Retrofit2.1.0源码里的类Retrofit.java赏析
猜你喜欢
转载自blog.csdn.net/m0_37271466/article/details/85088927
今日推荐
周排行