一. retrofit的簡(jiǎn)單使用
先看下涉及到的類:
首先定義個(gè)接口ApiService,所有的請(qǐng)求方法的定義.
public interface ApiService {
@POST("doPost")
Call<ResponseBody> doPost(@Query("name") String name,
@Query("email") String e
);
@GET("doGet")
Call<ResponseBody> doGet(@Query("name") String name,
@Query("age") int age
);
}
對(duì)retrofit的配置類,不建議每次都去創(chuàng)建retrofit的實(shí)例. OkHttpClient 也要保證整個(gè)工程只有一個(gè)實(shí)例,官方推薦也是這么做的.
/**
* Created by CaiRR on 2017-10-25.
*/
public class ApiClient {
private static OkHttpClient mOkHttpClient = null;
private static Retrofit mRetrofit;
private static ApiService mApiService;
private static final boolean isDebug = true;
private static final String HTTP_BASE_URL = "http://192.168.6.21:9090";
public static ApiClient getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final ApiClient INSTANCE = new ApiClient();
}
private ApiClient() {
initOkHttpClient();
mRetrofit = new Retrofit.Builder()
.baseUrl(HTTP_BASE_URL)
//設(shè)置 Json 轉(zhuǎn)換器
.addConverterFactory(GsonConverterFactory.create())
.client(mOkHttpClient)
.build();
mApiService = mRetrofit.create(ApiService.class);
}
private void initOkHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (isDebug) {
// Log信息攔截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//設(shè)置 Debug Log 模式
builder.addInterceptor(loggingInterceptor);
}
//設(shè)置超時(shí)
builder.connectTimeout(15, TimeUnit.SECONDS);
builder.readTimeout(60, TimeUnit.SECONDS);
builder.writeTimeout(60, TimeUnit.SECONDS);
//錯(cuò)誤重連
builder.retryOnConnectionFailure(true);
mOkHttpClient = builder.build();
}
public ApiService getApiService() {
return mApiService;
}
public OkHttpClient getOkhttpClient() {
if (mOkHttpClient == null) {
initOkHttpClient();
}
return mOkHttpClient;
}
}
在這里簡(jiǎn)單的使用 post 和 get 請(qǐng)求,分別用同步和異步的形式.
public class TestRetrofitActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_retrofit);
findViewById(R.id.btn_get).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doGet();
}
});
findViewById(R.id.btn_post).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doPost();
}
});
}
private void doPost() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Response<ResponseBody> response = ApiClient.getInstance().getApiService().doPost("xiaocai", "xiaocai@163.com").execute();
String data = response.body().string();
Log.d("xiaocai", data);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private void doGet() {
ApiClient.getInstance().getApiService().doGet("xiaocai", 25).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String data = response.body().string();
Log.d("xiaocai", data);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});
}
}
在spring boot中簡(jiǎn)單的做下處理:
@RestController
public class HelloController {
private final static org.slf4j.Logger log = LoggerFactory.getLogger("xiaocai");
@RequestMapping("/sayHello")
public String sayHello() {
return JsonHelper.success("hello world");
}
@GetMapping("/doGet")
public String testGet(@RequestParam(value = "name") String name,
@RequestParam(value = "age", required = false) int age) {
String data = "name:" + name + " age:" + age;
log.debug(data);
return JsonHelper.success(data);
}
@PostMapping("/doPost")
public String testPost(@RequestParam(value = "name") String name,
@RequestParam(value = "email", required = false) String email) {
String data = "name:" + name + " email:" + email;
log.debug(data);
return JsonHelper.success(data);
}
}
日志輸出:
// post 請(qǐng)求
D/OkHttp: --> POST http://192.168.6.21:9090/post?name=xiaocai&email=xiaocai@163.com http/1.1
D/OkHttp: Content-Length: 0
D/OkHttp: --> END POST (0-byte body)
D/OkHttp: <-- 200 http://192.168.6.21:9090/post?name=xiaocai&email=xiaocai@163.com (31ms)
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 82
D/OkHttp: Date: Wed, 25 Oct 2017 07:43:47 GMT
D/OkHttp: {"msg":"成功","code":0,"data":"name:xiaocai email:xiaocai@163.com","status":"0"}
D/OkHttp: <-- END HTTP (82-byte body)
D/xiaocai: {"msg":"成功","code":0,"data":"name:xiaocai email:xiaocai@163.com","status":"0"}
// get 請(qǐng)求
D/OkHttp: --> GET http://192.168.6.21:9090/get?name=xiaocai&age=25 http/1.1
D/OkHttp: --> END GET
D/OkHttp: <-- 200 http://192.168.6.21:9090/get?name=xiaocai&age=25 (18ms)
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 67
D/OkHttp: Date: Wed, 25 Oct 2017 07:44:13 GMT
D/OkHttp: {"msg":"成功","code":0,"data":"name:xiaocai age:25","status":"0"}
D/OkHttp: <-- END HTTP (67-byte body)
D/xiaocai: {"msg":"成功","code":0,"data":"name:xiaocai age:25","status":"0"}
整個(gè)過(guò)程還是比較清爽的.
其他的使用方式可以直接看官網(wǎng)的說(shuō)明:
retrofit的使用
二. 源碼分析
我們?cè)谑褂?retrofit 的時(shí)候,基本上都會(huì):
mRetrofit = new Retrofit.Builder()
.baseUrl(HTTP_BASE_URL)
//設(shè)置 Json 轉(zhuǎn)換器
.addConverterFactory(GsonConverterFactory.create())
.client(mOkHttpClient)
.build();
mApiService = mRetrofit.create(ApiService.class);
從 create 方法作為入口進(jìn)行分析:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service); // 校驗(yàn)是否為接口且不能繼承接口
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 使用動(dòng)態(tài)代理
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
// 每次請(qǐng)求都會(huì)走(執(zhí)行方法)
@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) { // 校驗(yàn)
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) { // 校驗(yàn)平臺(tái)
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 處理接口中定義的方法,serviceMethod解析注解
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
// args 是具體的參數(shù)值
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
// 里面涉及解析接口中定義的單個(gè)方法
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(); // 創(chuàng)建 ServiceMethod 類
serviceMethodCache.put(method, result); // 加入到緩存中
}
}
return result;
}
1. ServiceMethod 分析
ServiceMethod 的主要職責(zé)是將接口中的單個(gè)方法解析為 HTTP call ,里面涉及到解析請(qǐng)求方式,請(qǐng)求參數(shù).
需要注意的是 ServiceMethod 處理的單個(gè)接口中的方法.``````
ServiceMethod 是通過(guò) builder 模式創(chuàng)建的
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations(); // 獲取方法上的注解
this.parameterTypes = method.getGenericParameterTypes(); // 參數(shù)類型
this.parameterAnnotationsArray = method.getParameterAnnotations(); // 獲取參數(shù)中的注解及注解中的內(nèi)容
}
以下方法為例,主要研究方法上的注解及其值和解析方法參數(shù)的注解和值
@POST("post")
Call<ResponseBody> doPost(@Query("name") String name,
@Query("email") String email
);
methodAnnotations 中會(huì)得到 @retrofit2.http.POST(value=doPost) ,如果多個(gè)修飾這里也會(huì)得到多個(gè)注解類
parameterTypes 會(huì)得到 java.lang.String 和 java.lang.String 分別對(duì)應(yīng)兩個(gè)參數(shù)的類型
parameterAnnotationsArray 中會(huì)得到 類型是 Query 其值是 name,類型是 Query 其值是 email,這里的值是注解內(nèi)的值.
在 build 中會(huì)去解析注解:
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) { // 校驗(yàn)方法中的參數(shù)類型
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; // 解析方法中的參數(shù)
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); // 解析方法上的注解及注解值
}
解析方法上的注解:
private void parseMethodAnnotation(Annotation annotation) {
// 判斷請(qǐng)求類型
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
// 解析方法上的注解參數(shù)值 value:"doPost"
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError("URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value); // value:"favorite/add"
}
解析這個(gè)之后會(huì)用到 toRequest 方法中,在 OkHttpCall 中被調(diào)用.
在 parseParameterAnnotation 中會(huì)解析參數(shù)的注解,并構(gòu)建響應(yīng)的注解類型,在這個(gè)例子中會(huì)創(chuàng)建兩個(gè) Query 類. parseParameterAnnotation 中主要做了構(gòu)建對(duì)應(yīng)請(qǐng)求類型的類,這里就不貼代碼了.
簡(jiǎn)單看下其中一個(gè)請(qǐng)求類型的類 Field :
static final class Query<T> extends ParameterHandler<T> {
private final String name;// 參數(shù)名
private final Converter<T, String> valueConverter;
private final boolean encoded;
Query(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) return; // Skip null values.
// value:參數(shù)值,例如得到的是"xiaocai"
builder.addQueryParam(name, valueConverter.convert(value), encoded);
}
}
我們?cè)賮?lái)看下ServiceMethod對(duì)外提供的方法
toRequest 構(gòu)建請(qǐng)求體
/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
// 創(chuàng)建請(qǐng)求體
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) { // 校驗(yàn)參數(shù)個(gè)數(shù)是否一致
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
// 添加請(qǐng)求參數(shù)
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
toResponse 解析響應(yīng)體
/** Builds a method return value from an HTTP response body. */
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
這里就交給轉(zhuǎn)換器去解析請(qǐng)求體,可以看下一個(gè)最簡(jiǎn)單的解析 StringConverter:
static final class StringConverter implements Converter<String, String> {
static final StringConverter INSTANCE = new StringConverter();
@Override public String convert(String value) throws IOException {
return value;
}
}
parsePathParameters 解析參數(shù)
* Gets the set of unique path parameters used in the given URI. If a parameter is used twice
* in the URI, it will only show up once in the set.
*/
static Set<String> parsePathParameters(String path) {
Matcher m = PARAM_URL_REGEX.matcher(path);
Set<String> patterns = new LinkedHashSet<>();
while (m.find()) {
patterns.add(m.group(1));
}
return patterns;
}
到此我們已經(jīng)已經(jīng)分析完解析接口的操作,其中使用動(dòng)態(tài)代理解析接口,ServiceMethod 中解析單個(gè)接口中的方法的注解.
回到動(dòng)態(tài)代理中,ServiceMethod主要是給OkHttpCall調(diào)用.所以接下來(lái)分析OkHttpCall的作用.
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
這里調(diào)用了callAdapter中的adapt方法,而 CallAdapter 只是個(gè)接口,我的工程中使用到了rxjava,可以看下其實(shí)現(xiàn).
挑個(gè)簡(jiǎn)單的來(lái)看 SimpleCallAdapter:
static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
private final Type responseType;
private final Scheduler scheduler;
SimpleCallAdapter(Type responseType, Scheduler scheduler) {
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override public Type responseType() {
return responseType;
}
@Override public <R> Observable<R> adapt(Call<R> call) {
Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
.lift(OperatorMapResponseToBodyOrError.<R>instance());
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
具體的執(zhí)行就到 rxjava 中了,這部分以后在進(jìn)行分析.還是繼續(xù)解析 OkHttpCall 吧.
2. OkHttpCall 解析
OkHttpCall 其實(shí)是對(duì) okhttp 中的 Call 進(jìn)行包裝,真正實(shí)現(xiàn) Call 的還是 okhttp 中的 RealCall.
OkHttpCall 發(fā)起請(qǐng)求的方法:
@Override public synchronized Request request() {
okhttp3.Call call = rawCall;
if (call != null) { // 相當(dāng)于有緩存
return call.request();
}
if (creationFailure != null) { // 創(chuàng)建失敗的情況
if (creationFailure instanceof IOException) {
throw new RuntimeException("Unable to create request.", creationFailure);
} else {
throw (RuntimeException) creationFailure;
}
}
try {
// 創(chuàng)建 okhttp3.Call 對(duì)象并發(fā)起請(qǐng)求
return (rawCall = createRawCall()).request();
} catch (RuntimeException e) {
creationFailure = e;
throw e;
} catch (IOException e) {
creationFailure = e;
throw new RuntimeException("Unable to create request.", e);
}
}
創(chuàng)建 okhttp3.Call 對(duì)象
// 創(chuàng)建 okhttp3.Call 對(duì)象
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args); // 獲取請(qǐng)求體
// serviceMethod.callFactory 里保存的就是 okhttpclient
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
構(gòu)建請(qǐng)求體并發(fā)起請(qǐng)求,其實(shí)就是調(diào)用 okhttpclient 中的響應(yīng)的方法,也就體現(xiàn)了 OkHttpCall 是個(gè)包裝類.
OkHttpCall也分為同步和異步.先看同步請(qǐng)求方法:
@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 {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute()); // 執(zhí)行請(qǐng)求并解析響應(yīng)體
}
里面大多是校驗(yàn)判斷工作,createRawCall 也就是上面的創(chuàng)建請(qǐng)求體.
最后一行執(zhí)行請(qǐng)求,調(diào)用的是 okhttp3.Call 中的執(zhí)行方法,然后進(jìn)行解析響應(yīng)體:
Response<T> parseResponse(okhttp3.Response rawResponsy's source (the only stateful object) so we can pass the response along.
rawResponse = rawRe) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the bodesponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) { // 請(qǐng)求失敗
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse); // 返回失敗狀態(tài)的響應(yīng)體
} finally {
rawBody.close();
}
}
// 請(qǐng)求成功,但沒(méi)有響應(yīng)體.可以了解下這兩個(gè)狀態(tài)具體代表什么:
// HTTP 204(no content)表示響應(yīng)執(zhí)行成功铛嘱,但沒(méi)有數(shù)據(jù)返回仅颇,瀏覽器不用刷新度苔,不用導(dǎo)向新頁(yè)面仔粥。
// HTTP 205(reset content) 表示響應(yīng)執(zhí)行成功,重置頁(yè)面(Form表單)禀酱,方便用戶下次輸入读存。
if (code == 204 || code == 205) {
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody); // 交給 serviceMethod 處理響應(yīng)體
return Response.success(body, rawResponse); // 返回成功時(shí)的響應(yīng)體
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
還是那句話,OkHttpCall其實(shí)沒(méi)干什么大事,包裝類.
異步請(qǐng)求:
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("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(); // 構(gòu)建 okhttp3.Call 對(duì)象
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
// 調(diào)用 okhttp3.Call 對(duì)象的異步請(qǐng)求方法
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse); // 解析響應(yīng)體
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response); // 回調(diào)響應(yīng)成功的方法
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e); // 回調(diào)響應(yīng)失敗的方法
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e); // 回調(diào)響應(yīng)失敗的方法
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response); // 回調(diào)響應(yīng)失敗的方法
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
到此 OkHttpCall 整體已經(jīng)分析完成.
在整體看下 retrofit,涉及的類并不多
整體都是對(duì) okhttp 進(jìn)行封裝,是的調(diào)用起來(lái)更加清爽,而且擴(kuò)展性高,其中的轉(zhuǎn)換器可以自己添加.配合 rxjava 效果更佳.
看源碼時(shí)不僅理解原理,更重要的是學(xué)習(xí)代碼結(jié)構(gòu),看看大神們都是怎樣做封裝/解耦/擴(kuò)展性的.