尊重他人的劳动成果,转载请标明出处:http://blog.csdn.net/gengqiquan/article/details/52329259, 本文出自:【gengqiquan的博客】

不知不觉在在这家公司快三个月了,刚接手项目的时候是真的被里面的代码恶心到了,网络请求用的原生的httpURLconnection。这本来什么,关键是根本就没有进行任何封装。activity里面充斥着大量的handler,要找个oncreated()函数先得把类拉到一半,那种感觉岂止酸爽。由于项目周期紧张。没时间去大规模的重构,新框架只能在新功能里写。采用了retrofit,初期做了简单的封装,断断续续一段时间的优化整理。现在分享出来给大家。

为了方便大家,会形成依赖库。引入和调用方式请看文章底部的github文档
本次封装需要做到的

1支持异步请求统一回调
2参数可配置
3链式调用
4支持基本get和post封装
5支持rxjava返回
6支持一个接口Service 对应一种请求类型,而不是每个API都需要去定义一个接口方法
7支持取消单个请求
8支持请求打标签,退出页面时取消当前页面所有请求
9支持动态配置缓存,缓存与具体的请求接口绑定,
10支持追加统一参数,登录信息token、设备号等
11支持接口版本号配置
12流式下载文件,取消下载请求

下面我们来看下封装过程,博客底部会贴出示例项目地址
retrofit独树一帜的把请求采用了接口,方法和注解参数(parameter annotations)来声明式定义一个请求应该如何被创建的方式。
像这样

public interface GitHub {@GET("/repos/{owner}/{repo}/contributors")List<Contributor> contributors(@Path("owner")String owner, @Path("repo")String repo);

然后去实例化并且调用请求

  GitHub github = restAdapter.create(GitHub.class);// Fetch and print a list of the contributors to this library.List<Contributor> contributors = github.contributors("square","retrofit");

先不谈retrofit到底做了多少优化、性能上有多少提升。光是这样的调用方式我就受不了。我特么得写多少个 像上面的GitHub 一样的Service,就算是把url注解方法都写在一个里面,那也得多少行?一个项目六七十行应该是没什么问题的了。嗯。反正我会看疯了的。而且这样的一种调用方式是直接面向框架层的,以后万一我们换框架了怎么办?代码挨个找出来全换一边?你疯不疯?
那我们有没有什么办法改变他?很简单,我们在框架外面再套一层通用框架,作为框架的设计者,我们应该让调用者知道怎么调用就可以了,而不应该让调用者去考虑底层实现的细节。
好在retrofit提供了Url 参数替换注解@Url String url,通过这个注解我们可以动态的设置请求的url。
下面列出一些简单的参数注解

@Url 替换url
@QueryMap  替换url中查询参数
@Header  替换header
@FieldMap 替换post请求body中参数
@FormUrlEncoded post请求需要加的方法注解
@POST() 标示该方法为post请求
@GET() 标示该方法为get请求

了解了这些注解这样我们就可以将我们项目的请求变成几个基本的方法,由于我的项目的服务端返回的基本格式不是固定的,而GsonConverterFactory,对于retrofit的返回类型要求只能是: 具体对象、jsonObject、jsonArray这三种 ,为了避免每个请求写两个方法,这里我自己写了个返回类型为String的解析器,参考 Retrofit自定义Converter之StringConverterFactory

这样我们的返回类型都是String

public interface RetrofitHttpService {@GET()Call<String> get(@Url String url, @QueryMap Map<String, String> params, @Header("Cache-Time") String time);@FormUrlEncoded@POST()Call<String> post(@Url String url, @FieldMap Map<String, String> params, @Header("Cache-Time") String time);}

如果你项目的返回请求外层有固定的格式可以把 Call<JsonObject>替换成Call<Model>,这里的model就是你的基础数据返回类型。
如果你要使用rxjava的话需要额外两个注解方法

@GET()Observable<String> Obget(@Url String url, @QueryMap Map<String, String> params, @Header("Cache-Time") String time);@FormUrlEncoded@POST()Observable<String> Obpost(@Url String url, @FieldMap Map<String, String> params, @Header("Cache-Time") String time);

构建网络数据请求类,这里为了方便不同的项目使用,用一个builder类来构建单例

public class HttpUtil {private static volatile HttpUtil mInstance;private static volatile RetrofitHttpService mService;private Context mAppliactionContext;private static String mVersionApi;private ParamsInterceptor mParamsInterceptor;//构造函数私有,不允许外部调用private HttpUtil(RetrofitHttpService mService, Context mAppliactionContext, String mVersionApi, ParamsInterceptor mParamsInterceptor) {this.mService = mService;this.mAppliactionContext = mAppliactionContext;this.mVersionApi = mVersionApi;this.mParamsInterceptor = mParamsInterceptor;}public static class SingletonBuilder {private Context appliactionContext;private String baseUrl;private List<String> servers = new ArrayList<>();private String versionApi;private ParamsInterceptor paramsInterceptor;private List<Converter.Factory> converterFactories = new ArrayList<>();private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();OkHttpClient client;public SingletonBuilder(Context context) {try {//防止传入的是activity的上下文Activity activity = (Activity) context;appliactionContext = context.getApplicationContext();} catch (Exception e) {e.printStackTrace();appliactionContext = context;}}public SingletonBuilder client(OkHttpClient client) {this.client = client;return this;}public SingletonBuilder versionApi(String versionApi) {this.versionApi = versionApi;return this;}public SingletonBuilder paramsInterceptor(ParamsInterceptor interceptor) {this.paramsInterceptor = interceptor;return this;}public SingletonBuilder baseUrl(String baseUrl) {this.baseUrl = baseUrl;return this;}public SingletonBuilder addServerUrl(String ipUrl) {this.servers.add(ipUrl);return this;}public SingletonBuilder serverUrls(List<String> servers) {this.servers = servers;return this;}public SingletonBuilder addConverterFactory(Converter.Factory factory) {this.converterFactories.add(factory);return this;}public SingletonBuilder addCallFactory(CallAdapter.Factory factory) {this.adapterFactories.add(factory);return this;}public HttpUtil build() {if (checkNULL(this.baseUrl)) {throw new NullPointerException("BASE_URL can not be null");}if (converterFactories.size() == 0) {converterFactories.add(StringConverterFactory.create());}if (adapterFactories.size() == 0) {adapterFactories.add(RxJavaCallAdapterFactory.create());}if (client == null) {client = OkhttpProvidede.okHttpClient(appliactionContext, baseUrl, servers);}Retrofit.Builder builder = new Retrofit.Builder();for (Converter.Factory converterFactory : converterFactories) {builder.addConverterFactory(converterFactory);}for (CallAdapter.Factory adapterFactory : adapterFactories) {builder.addCallAdapterFactory(adapterFactory);}Retrofit retrofit = builder.baseUrl(baseUrl + "/").client(client).build();RetrofitHttpService retrofitHttpService =retrofit.create(RetrofitHttpService.class);mInstance = new HttpUtil(retrofitHttpService, appliactionContext, versionApi, paramsInterceptor);return mInstance;}}

一般网络请求都是异步的,那么此时我们进行异步调用网络请求的代码是这样的

   HttpUtil.getmInstance(mContext).get("url",params,"3600*24").enqueue(new Callback<JsonObject>() {@Overridepublic void onResponse(Call<JsonObject> call, Response<JsonObject> response) {}@Overridepublic void onFailure(Call<JsonObject> call, Throwable t) {}});

以上的代码已经采用了回调的方式,保证了onResponse()和 onFailure()里的代码都是在主线程里执行的,这样的代码相对于handler那种已经简洁易用了很多,不过我们不能满足于此,比如判断单例是否实例化并去获取单例的方法根本没必要每次调用都去做,我们只需要在application里初始化就可以了。回调的参数是Call 和Response ,我们肯定需要对他做同样的处理拿出里面的json值才能用的。这样这些代码就可以进一步封装。
retrofit里面提供了异步回调接口类Callback<T>,不过我们不能直接拿来用,因为一旦我们换网络请求框架。肯定是不能再使用旧的框架里面的回调接口的,所以需要我们定义自己的外层回调接口,一般有成功的回调和失败的回调两种。由于我支持lambda,所以我会把不同的情况分别定义成接口。如何在android中使用lambda请看这篇Androidstudio使用java8、lambda表达式教程
成功回调,一般就是返回的json串,一个参数就够了

@FunctionalInterface
public interface Success {void Success( String model);
}

失败回调,失败回调不同的网络框架返回的参数个数不一样,这里我们定义成Object数组

@FunctionalInterface
public interface Error {void Error(Object... values);
}

下面我们封装post请求

  public static void post(String url, Map<String, String> params, String cacheTime,Success mSuccessCallBack ,Error mErrorCallBack){mInstance.post(url, params, cacheTime).enqueue(new Callback<String>() {@Overridepublic void onResponse(Call<String> call, Response<String> response) {if (response.code() == 200) {mSuccessCallBack.Success(response.body().toString());} else {mErrorCallBack.Error(response.code(), message(response.message()), null);}}@Overridepublic void onFailure(Call<String> call, Throwable t) {mErrorCallBack.Error(200, message(t.getMessage()), t);}});}

此时我们调用post请求时是这样的

  HttpUtil.get("url",params,"3600*24", new Success() {@Overridepublic void Success(String model) {}},new Error() {@Overridepublic void Error(Object... value) {}});

独立回调的方式,使用过Volley的人应该熟悉这种方式,这样我们每次调用post的时候只要调用这个方法就可以了,其他几个请求方法一样的封装方式。
但是这样我还是觉得不够简洁,不够优雅,必传参数太多了(哪怕传入null)。两个回调函数看起来也太臃肿了(接口方法增加了代码长度,当然可以用lambda优化,不过那样看起来不伦不类,其实并不利于阅读)。有时候我们是只有一个url的,有时候我们却还会有查询参数的,有时候我们的url是要追加版本号的,有时候我们是需要当前接口支持缓存的,而有的项目是需要统一追加各种配置参数的,如设备号,手机号码等等。这样我们的请求方法的参数个数就是不固定的,比较暴力的方法就是写多个不同参数的重载方法。我以前一般是这么干的,不过太low,这里我们采用builder模式。

  public static class Builder {Map<String, String> params = new HashMap<>();String url;Error mErrorCallBack;Success mSuccessCallBack;String cacheTime;boolean addVersion = false;public Builder CacheTime(String time) {this.cacheTime = time;return this;}public Builder Url(String url) {this.url = url;return this;}public Builder Params(Map<String, String> params) {this.params.putAll(params);return this;}public Builder Params(String key, String value) {this.params.put(key, value);return this;}public Builder Success(Success success) {this.mSuccessCallBack = success;return this;}public Builder Version() {this.addVersion = true;return this;}public Builder Error(Error error) {this.mErrorCallBack = error;return this;}public Builder() {this.setParams();}public Builder(String url) {this.setParams(url);}private void setParams() {this.setParams(null);}private void setParams(String url) {if (mInstance == null) {throw new NullPointerException("HttpUtil has not be initialized");}this.url = url;this.params = new HashMap<>();this.mErrorCallBack = (v) -> {};this.mSuccessCallBack = (s) -> {};}private String checkUrl(String url) {if (Util.checkNULL(url)) {throw new NullPointerException("absolute url can not be empty");}if (addVersion && !url.contains(mVersionApi)) {url = V(url);}return url;}public void get() {this.url = checkUrl(this.url);this.params = checkParams(this.params);if (cacheTime == null) {cacheTime = "";}mInstance.get(url, checkParams(params), cacheTime).enqueue(new Callback<String>() {@Overridepublic void onResponse(Call<String> call, Response<String> response) {if (response.code() == 200) {mSuccessCallBack.Success(response.body().toString());} else {mErrorCallBack.Error(response.code(), message(response.message()), null);}}@Overridepublic void onFailure(Call<String> call, Throwable t) {mErrorCallBack.Error(200, message(t.getMessage()), t);}});}}

checkParams()方法就是对参数的统一处理,追加参数什么的可以在这里面做,Version()方法 是决定要不要给url追加开发版本号。写了这么一大堆,貌似代码变多了啊。别担心,下面我们看看当我们调用请求的时候的代码是多少

  new HttpUtil.Builder("favorite_authorized/list?page=1").Version().CacheTime("3600*24").Params("carId", sb.toString()).Params(sortMap_).Success((s) -> {ld_.dismiss();BaseModel model = new BaseModel(s);}).Error((v) -> {ld_.dismiss();handler_.obtainMessage(MSG, v[1]).sendToTarget();}).get();});

就这么多,采用链式调用,上面我们动态设置了请求的url,参数,追加开发版本号,追加参数,设置了缓存一天,代码简洁明了,实际使用中我们可以根据单个请求的不同需求决定添加什么方法链。
如果你的项目需要支持缓存,请参照okhttp之自定义Interceptor:缓存拦截器 这篇博客
如果你项目需要失败重试和切换服务器IP并且服务端不支持需要客户端配置实现,请参照 okhttp之自定义Interceptor:请求失败切换IP重试拦截器

下面我们添加取消请求

/*
*添加某个请求
*@author Administrator
*@date 2016/10/12 11:00
*/private static synchronized void putCall(Object tag, String url, Call call) {if (tag == null)return;synchronized (CALL_MAP) {CALL_MAP.put(tag.toString() + url, call);}}/**取消某个界面都所有请求,或者是取消某个tag的所有请求* 如果要取消某个tag单独请求,tag需要转入tag+url*@author Administrator*@date 2016/10/12 10:57*/public static synchronized void cancel(Object tag) {if (tag == null)return;List<String> list = new ArrayList<>();synchronized (CALL_MAP) {for (String key : CALL_MAP.keySet()) {if (key.startsWith(tag.toString())) {CALL_MAP.get(key).cancel();list.add(key);}}}for (String s : list) {removeCall(s);}}/**移除某个请求*@author Administrator*@date 2016/10/12 10:58*/private static synchronized void removeCall(String url) {synchronized (CALL_MAP) {for (String key : CALL_MAP.keySet()) {if (key.contains(url)) {url = key;break;}}CALL_MAP.remove(url);}}

在请求类builder构建时添加进tag

  Object tag;public Builder Tag(Object tag) {this.tag = tag;return this;}

在call构建时将添加进全局静态map,请求返回时移除掉,免得持有引用导致内存泄漏

  public void get() {if (cacheTime == null) {cacheTime = "";}Call call = mInstance.get(checkUrl(this.url), checkParams(params), cacheTime);putCall(tag, url, call);//将call添加进mapcall.enqueue(new Callback<String>() {@Overridepublic void onResponse(Call<String> call, Response<String> response) {if (response.code() == 200) {mSuccessCallBack.Success(response.body().toString());} else {mFailureCallBack.Failure(response.code(), message(response.message()), null);mErrorCallBack.Error(response.code(), message(response.message()), null);}if (tag != null)removeCall(url);//移除call}@Overridepublic void onFailure(Call<String> call, Throwable t) {mFailureCallBack.Failure(200, message(t.getMessage()), t);mErrorCallBack.Error(200, message(t.getMessage()), t);if (tag != null)removeCall(url);//移除call}});}

下面贴出完整的httputil类

package com.sunshine.retrofit;import android.app.Activity;
import android.content.Context;
import android.widget.Toast;import com.sunshine.retrofit.converter.StringConverterFactory;
import com.sunshine.retrofit.interfaces.Error;
import com.sunshine.retrofit.interfaces.ParamsInterceptor;
import com.sunshine.retrofit.interfaces.Success;
import com.sunshine.retrofit.utils.OkhttpProvidede;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.Converter;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;/*** Created by 耿 on 2016/6/28.*/
public class HttpUtil {private static volatile HttpUtil mInstance;private static volatile RetrofitHttpService mService;private Context mAppliactionContext;private static String mVersionApi;private ParamsInterceptor mParamsInterceptor;//构造函数私有,不允许外部调用private HttpUtil(RetrofitHttpService mService, Context mAppliactionContext, String mVersionApi, ParamsInterceptor mParamsInterceptor) {this.mService = mService;this.mAppliactionContext = mAppliactionContext;this.mVersionApi = mVersionApi;this.mParamsInterceptor = mParamsInterceptor;}public static RetrofitHttpService getService() {if (mInstance == null) {throw new NullPointerException("HttpUtil has not be initialized");}return mService;}public static class SingletonBuilder {private Context appliactionContext;private String baseUrl;private List<String> servers = new ArrayList<>();private String versionApi;private ParamsInterceptor paramsInterceptor;private List<Converter.Factory> converterFactories = new ArrayList<>();private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();OkHttpClient client;public SingletonBuilder(Context context) {try {//防止传入的是activity的上下文Activity activity = (Activity) context;appliactionContext = context.getApplicationContext();} catch (Exception e) {e.printStackTrace();appliactionContext = context;}}public SingletonBuilder client(OkHttpClient client) {this.client = client;return this;}public SingletonBuilder versionApi(String versionApi) {this.versionApi = versionApi;return this;}public SingletonBuilder paramsInterceptor(ParamsInterceptor interceptor) {this.paramsInterceptor = interceptor;return this;}public SingletonBuilder baseUrl(String baseUrl) {this.baseUrl = baseUrl;return this;}public SingletonBuilder addServerUrl(String ipUrl) {this.servers.add(ipUrl);return this;}public SingletonBuilder serverUrls(List<String> servers) {this.servers = servers;return this;}public SingletonBuilder addConverterFactory(Converter.Factory factory) {this.converterFactories.add(factory);return this;}public SingletonBuilder addCallFactory(CallAdapter.Factory factory) {this.adapterFactories.add(factory);return this;}public HttpUtil build() {if (checkNULL(this.baseUrl)) {throw new NullPointerException("BASE_URL can not be null");}if (converterFactories.size() == 0) {converterFactories.add(StringConverterFactory.create());}if (adapterFactories.size() == 0) {adapterFactories.add(RxJavaCallAdapterFactory.create());}if (client == null) {client = OkhttpProvidede.okHttpClient(appliactionContext, baseUrl, servers);}Retrofit.Builder builder = new Retrofit.Builder();for (Converter.Factory converterFactory : converterFactories) {builder.addConverterFactory(converterFactory);}for (CallAdapter.Factory adapterFactory : adapterFactories) {builder.addCallAdapterFactory(adapterFactory);}Retrofit retrofit = builder.baseUrl(baseUrl + "/").client(client).build();RetrofitHttpService retrofitHttpService =retrofit.create(RetrofitHttpService.class);mInstance = new HttpUtil(retrofitHttpService, appliactionContext, versionApi, paramsInterceptor);return mInstance;}}public static String V(String url) {if (checkNULL(mVersionApi)) {throw new NullPointerException("can not add VersionApi ,because of VersionApi is null");}if (!url.contains(mVersionApi)) {return mVersionApi + url;}return url;}public static Map<String, String> checkParams(Map<String, String> params) {if (params == null) {params = new HashMap<>();}if (mInstance.mParamsInterceptor != null) {params = mInstance.mParamsInterceptor.checkParams(params);}//retrofit的params的值不能为null,此处做下校验,防止出错for (Map.Entry<String, String> entry : params.entrySet()) {if (entry.getValue() == null) {params.put(entry.getKey(), "");}}return params;}// 判断是否NULLpublic static boolean checkNULL(String str) {return str == null || "null".equals(str) || "".equals(str);}// 判断是否NULLpublic static void Error(Context context, String msg) {if (checkNULL(msg)) {msg = "似乎已断开与互联网连接";}Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();}public static String message(String mes) {if (checkNULL(mes)) {mes = "似乎已断开与互联网连接";}if (mes.equals("timeout") || mes.equals("SSL handshake timed out")) {return "网络请求超时";} else {return mes;}}static Map<String, Call> CALL_MAP = new HashMap<>();/**添加某个请求*@author Administrator*@date 2016/10/12 11:00*/private static synchronized void putCall(Object tag, String url, Call call) {if (tag == null)return;synchronized (CALL_MAP) {CALL_MAP.put(tag.toString() + url, call);}}/**取消某个界面都所有请求,或者是取消某个tag的所有请求* 如果要取消某个tag单独请求,tag需要转入tag+url*@author Administrator*@date 2016/10/12 10:57*/public static synchronized void cancel(Object tag) {if (tag == null)return;List<String> list = new ArrayList<>();synchronized (CALL_MAP) {for (String key : CALL_MAP.keySet()) {if (key.startsWith(tag.toString())) {CALL_MAP.get(key).cancel();list.add(key);}}}for (String s : list) {removeCall(s);}}/**移除某个请求*@author Administrator*@date 2016/10/12 10:58*/private static synchronized void removeCall(String url) {synchronized (CALL_MAP) {for (String key : CALL_MAP.keySet()) {if (key.contains(url)) {url = key;break;}}CALL_MAP.remove(url);}}public static class Builder {Map<String, String> params = new HashMap<>();String url;Error mErrorCallBack;Success mSuccessCallBack;String cacheTime;boolean addVersion = false;Object tag;public Builder CacheTime(String time) {this.cacheTime = time;return this;}public Builder Url(String url) {this.url = url;return this;}public Builder Tag(Object tag) {this.tag = tag;return this;}public Builder Params(Map<String, String> params) {this.params.putAll(params);return this;}public Builder Params(String key, String value) {this.params.put(key, value);return this;}public Builder Success(Success success) {this.mSuccessCallBack = success;return this;}public Builder Version() {this.addVersion = true;return this;}public Builder Error(Error error) {this.mErrorCallBack = error;return this;}public Builder() {this.setParams();}public Builder(String url) {this.setParams(url);}private void setParams() {this.setParams(null);}private void setParams(String url) {if (mInstance == null) {throw new NullPointerException("HttpUtil has not be initialized");}this.url = url;this.params = new HashMap<>();this.mErrorCallBack = (v) -> {};this.mSuccessCallBack = (s) -> {};}private String checkUrl(String url) {if (checkNULL(url)) {throw new NullPointerException("absolute url can not be empty");}if (addVersion) {url = mInstance.V(url);}return url;}public void get() {if (cacheTime == null) {cacheTime = "";}Call call = mService.get(checkUrl(this.url), checkParams(params), cacheTime);putCall(tag, url, call);call.enqueue(new Callback<String>() {@Overridepublic void onResponse(Call<String> call, Response<String> response) {if (response.code() == 200) {mSuccessCallBack.Success(response.body().toString());} else {mErrorCallBack.Error(response.code(), message(response.message()), null);}if (tag != null)removeCall(url);}@Overridepublic void onFailure(Call<String> call, Throwable t) {mErrorCallBack.Error(200, message(t.getMessage()), t);if (tag != null)removeCall(url);}});}public void post() {if (cacheTime == null) {cacheTime = "";}Call call = mService.post(checkUrl(this.url), checkParams(params), cacheTime);putCall(tag, url, call);call.enqueue(new Callback<String>() {@Overridepublic void onResponse(Call<String> call, Response<String> response) {if (response.code() == 200) {mSuccessCallBack.Success(response.body().toString());} else {mErrorCallBack.Error(response.code(), message(response.message()), null);}if (tag != null)removeCall(url);}@Overridepublic void onFailure(Call<String> call, Throwable t) {mErrorCallBack.Error(200, message(t.getMessage()), t);if (tag != null)removeCall(url);}});}public Observable<String> Obget() {this.url = checkUrl(this.url);this.params = checkParams(this.params);if (cacheTime == null) {cacheTime = "";}return mService.Obget(url, checkParams(params), cacheTime);}public Observable<String> Obpost() {this.url = checkUrl(this.url);this.params = checkParams(this.params);if (cacheTime == null) {cacheTime = "";}return mService.Obpost(url, checkParams(params), cacheTime);}/**按基础格式返回的数据进行预处理*只返回status为true的情况下的data*@author Administrator*@date 2016/10/20 11:31*/public Observable<String> getModelData(Context context) {return Obget().map(s -> new BaseModel(s)).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).filter(model -> model.trueStatus(context))//可能有toast操作,必须在主线程.map(model -> model.data);}public Observable<String> postModelData(Context context) {return Obpost().map(s -> new BaseModel(s)).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).filter(model -> model.trueStatus(context)).map(model -> model.data);}}
}

以上就是本次retrofit的封装的完整实现了

完整的示例项目地址https://github.com/gengqiquan/HttpUtil.git

有什么建议的可以留言喔

如果我的博客对您有帮助,请留言鼓励下或者点个赞吧!

我建了一个QQ群(群号:121606151),用于大家讨论交流Android技术问题,有兴趣的可以加下,大家一起进步。

浅谈Retrofit封装-让框架更加简洁易用相关推荐

  1. php tp框架,浅谈PHP之ThinkPHP框架使用详解

    Thinkphp框架其精髓就在于实现了MVC思想,其中M为模板.V为视图.C为控制器,模板一般是公共使用类,在涉及数据库时,一般会跟数据表同名,视图会和控制器类里的方法进行名字的一一对应. 下载及配置 ...

  2. 浅谈五大Python Web框架

    http://www.csdn.net/article/2011-02-17/292058 导读:作者飞龙写了一篇<浅谈Python Web框架>,文中他介绍了几个Python Web框架 ...

  3. python 前端框架比较_浅谈五大Python Web框架

    说到Web Framework,Ruby的世界Rails一统江湖,而Python则是一个百花齐放的世界,各种micro-framework.framework不可胜数,不完全列表见: 虽然另一大脚本语 ...

  4. v浅谈vue之vant框架

    目录 vant框架的使用 使用一个Tabbar 标签栏 App.vue 封装为一个MainTabbar.vue组件 使用一个Swipe 轮播图 **Home.vue**组件使用轮播图 使用一个 Gir ...

  5. 浅谈PHP几款框架的优缺点,PHP主流框架有什么优缺点

    PHP主流框架有什么优缺点 发布时间:2020-07-20 14:10:28 来源:亿速云 阅读:55 作者:Leah 这期内容当中小编将会给大家带来有关PHP主流框架有什么优缺点,文章内容丰富且以专 ...

  6. php组件是啥,浅谈PHP组件、框架以及Composer

    本篇文章主要介绍了PHP组件.框架以及Composer,具有一定的学习价值,感兴趣的朋友可以了解一下. 什么是组件 组件是一组打包的代码,是一系列相关的类.接口和Trait,用于帮助我们解决PHP应用 ...

  7. pythonweb框架_浅谈python web三大框架

    Django请求回应机制 Django(ORM.URL分发系统.MTV ) Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初 ...

  8. 浅谈opencl之整体框架

    结合四大模型,以CPU和GPU平台为例子,opencl整体框架如下: CPU为host端, GPU为device端, host和device端的交互是以context为主.context贯穿到整个op ...

  9. 框架层 java_简单就是美!浅谈java各层框架。

    很久没有来过Jdon了,感觉陌生了许多,也许有人看了我的标题以为我是一个ROR的鼓吹手,其实,刚好相反,基于现在开发的现状,我觉得JAVA阵营有必要站稳阵脚,分析清楚技术的走向,毕竟都涉及到我们的饭碗 ...

最新文章

  1. 【Luogu3041】视频游戏的连击(AC自动机,动态规划)
  2. mysql数据库实验报告jdbc_Jdbc连接数据库实验报告(2)
  3. sql获取一张表所有的字段_SQL语句19问
  4. lsof根据端口查进程
  5. mysql查询职位大于3_4、MySQL查询(控制行)
  6. TFS2010迁移后Web工作项访问提示:error HRESULT E_FAIL has been returned from a call to a COM component....
  7. php for next,Nextcloud停留无限登录页面 PHP7的问题及解决方案
  8. mysql 读写引擎_揭秘MySQL存储引擎spider
  9. 12c表空间不存在_一文看懂Oracle查询表空间的每日增长量和历史情况统计
  10. 内核并发控制---读写信号量(来自网易)
  11. H.264 无参考视频质量评价方法 (使用了基于遗传编程方法的符号回归)
  12. 2020年日历电子版(打印版)_2020年日历打印版下载
  13. 【Python】py3.6请求网站时报错:http.client.RemoteDisconnected: Remote end closed connection without response
  14. 多台机器同步Windows上的文件 -cwRsync 同步问题
  15. Unity3D-----摄像机镜头移动并限制角度
  16. Django的BUG:ImportError: cannot import name 'patterns'
  17. 【自然语言处理】一篇文章入门分词(Tokenization)
  18. html5 讯飞离线语音包,讯飞输入法离线语音怎么用?讯飞输入法离线语音开启方法...
  19. 传奇开区发布广告和选择广告投放网站的那些事
  20. 004永磁同步电机的工作原理:大白话详细讲解从最简单的直流有刷电机到永磁同步电机是如何转动起来的

热门文章

  1. 使用Visual Paradigm如何复制表格
  2. 华为linux连蓝牙鼠标,huawei蓝牙鼠标怎么样? 华为蓝牙鼠标连接使用教程
  3. Python基础入门知识(2)
  4. TC27x寄存器学习
  5. python应用——简单的跟随北上资金策略
  6. 利用Windows11安卓子系统对APP进行渗透测试
  7. H5满屏彩色泡泡小特效(适合表白哦~做完发给让你每天想念的人吧~)
  8. 汇编语言实验八核心考点
  9. 京东运营 不错的帖子
  10. 控件中一些常用的属性和事件