对okhttp的二次封装
2017-02-22 12:48
323 查看
okhttp大家都很熟悉了,但是如果没有一个好的封装的话,每次都会写很多冗余的代码,而且如果以后项目中不再使用okHttp了,那么应用层需要做大量的修改,所以这样维护起来太差了,需要好好封装一下。封装后达到的效果如下:
通过一行代码的调用发送请求自定义callback,处理回调结果,可以将json转化成实体类
支持 https
先看看运行效果:
![](https://raw.githubusercontent.com/ge1944633835/Gdroid/master/ScreenShut/okhttpRequest.gif)
进行封装之前,需要梳理一下okhttp知识,我画了一张图:
![](https://raw.githubusercontent.com/ge1944633835/Gdroid/master/ScreenShut/okhttp.png)
可以看到整个okhttp最核心的三点,分别是request、okHttpClient、callBack。 request可以携带一些参数,然后通过okHttpClient发送请求到服务端,然后通过callback回调给我们数据或者异常信息。所以封装的时候,也应该从这三点考虑,观察相同点与不同点,从而进行封装
一般情况下,okhttp发送一个get请求是这样的:
//创建okHttpClient对象 OkHttpClient mOkHttpClient = new OkHttpClient(); //创建一个Request final Request request = new Request.Builder() .url("https://github.com/") .build(); //new call Call call = mOkHttpClient.newCall(request); //请求加入调度 call.enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { } @Override public void onResponse(final Response response) throws IOException { //String htmlStr = response.body().string(); } });
这还是最简单的发送请求,也没对okHttpClient进行配置,如果项目中有多处要使用okhttp进行网络请求,会造成代码冗余。下面开始封装:
首先封装一个
CommonRequest创建request对象
/** * Created by gechuanguang on 2017/2/17. * 邮箱:1944633835@qq.com * @function 接收请求参数,为我们生成Request对象 */ public class CommonRequest { /** * @param url * @param params * @return 返回一个创建好post的Request对象 */ public static Request createPostRequest(String url, RequestParams params){ FormBody.Builder mFormBodybuilder = new FormBody.Builder(); if(params!=null){ for(Map.Entry<String,String> entry: params.urlParams.entrySet()){ // 将请求参数逐一添加到请求体中 mFormBodybuilder.add(entry.getKey(),entry.getValue()); } } FormBody mFormBody=mFormBodybuilder.build(); return new Request.Builder() .url(url) .post(mFormBody) .build(); } /** * @param url * @param params * @return 返回一个创建好get的Request对象 */ public static Request createGetRequest(String url, RequestParams params){ StringBuilder urlBuilder=new StringBuilder(url).append("?"); if(params!=null){ for(Map.Entry<String,String> entry: params.urlParams.entrySet()){ // 将请求参数逐一添加到请求体中 urlBuilder.append(entry.getKey()).append("=") .append(entry.getValue()) .append("&"); } } return new Request.Builder() .url(urlBuilder.substring(0,urlBuilder.length()-1)) //要把最后的&符号去掉 .get() .build(); } /** * 文件上传请求 * * @return */ private static final MediaType FILE_TYPE = MediaType.parse("application/octet-stream"); public static Request createMultiPostRequest(String url, RequestParams params) { MultipartBody.Builder requestBody = new MultipartBody.Builder(); requestBody.setType(MultipartBody.FORM); if (params != null) { for (Map.Entry<String, Object> entry : params.fileParams.entrySet()) { if (entry.getValue() instanceof File) { requestBody.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + entry.getKey() + "\""), RequestBody.create(FILE_TYPE, (File) entry.getValue())); } else if (entry.getValue() instanceof String) { requestBody.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + entry.getKey() + "\""), RequestBody.create(null, (String) entry.getValue())); } } } return new Request.Builder() .url(url) .post(requestBody.build()) .build(); } }
CommonRequest包含了 创建好post的Request对象、 创建好get的Request对象、 以及创建好文件上传的Request对象,这样通过CommonRequest调用对应的静态方法即可得到对应的request对象。接下来就可以封装okhttpClient来发送请求
封装
CommonOkhttpClient
/** * Created by gechuanguang on 2017/2/17. * 邮箱:1944633835@qq.com * @function 创建okHttpclient对象,并且配置支持https,以及发送请求 */ public class CommonOkhttpClient { private static final int TIME_OUT = 30; private static OkHttpClient mOkHttpClient=null; //为mOkHttpClient去配置参数 类加载的时候开始创建静态代码块,并且只执行一次 static { OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder(); okHttpClientBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS); okHttpClientBuilder.writeTimeout(TIME_OUT, TimeUnit.SECONDS); okHttpClientBuilder.readTimeout(TIME_OUT, TimeUnit.SECONDS); okHttpClientBuilder.followRedirects(true); //设置重定向 其实默认也是true /*--添加请求头 这个看个人需求 --*/ // okHttpClientBuilder.addInterceptor(new Interceptor() { // @Override // public Response intercept(Chain chain) throws IOException { // Request request = chain.request() // .newBuilder() // .addHeader("User-Agent", "Android—Mobile") // 标明发送本次请求的客户端 // .build(); // return chain.proceed(request); // } // }); //添加https支持 okHttpClientBuilder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); /** * trust all the https point */ okHttpClientBuilder.sslSocketFactory(HttpsUtils.initSSLSocketFactory(),HttpsUtils.initTrustManager()); mOkHttpClient = okHttpClientBuilder.build(); } /** * 发送具体的http/https的请求 * @param request * @param commonCallback * @return Call */ public static Call sendRequest(Request request, CommonJsonCallback commonCallback){ Call call=mOkHttpClient.newCall(request); call.enqueue(commonCallback); return call; } /** * 发送具体的http/https的请求 * @param request * @param commonCallback * @return Call */ public static Call sendRequest(Request request, Callback commonCallback){ Call call=mOkHttpClient.newCall(request); call.enqueue(commonCallback); return call; } }
这个CommonOkhttpClient中配置好了okHttpClient,可以支持https请求,如果使用的是Retrofit网络请求框架的话,可以设置这个okhttpClient配置的,这样retrofit也是支持https的。还有一点需要注意,我并没有使用默认的CallBack回调,而是封装了一层CommonJsonCallback,
因为默认的Callback是回调在子线程的,在更新UI的时候还需要切换到主线程,而且多数时候还需要将Json字符串转化成实体类,使用默认的Callback非常不方便。所以我是将这些操作封装到CommonJsonCallback来完成,避免每次切换线程以及将json转化成实体类重复的操作。(ps:有个问题值得思考,为啥okhttp callback是回调在子线程的。个人感觉之所以回调在子线程,应该是有他的道理的,比如说我们下载一个很大文件,那么回调的这个response数据肯定很大,肯定需要以流的形式转移到手机SD卡,需要在子线程边下载边转移,避免内存溢出)
CommonJsonCallback专门处理JSON的回调
public class CommonJsonCallback implements Callback { /** * the logic layer exception, may alter in different app */ protected final String RESULT_CODE = "ecode"; // 有返回则对于http请求来说是成功的,但还有可能是业务逻辑上的错误 protected final int RESULT_CODE_VALUE = 0; protected final String ERROR_MSG = "emsg"; protected final String EMPTY_MSG = ""; protected final String COOKIE_STORE = "Set-Cookie"; // decide the server it // can has the value of // set-cookie2 /** * the java layer exception, do not same to the logic error */ protected final int NETWORK_ERROR = -1; // the network relative error protected final int JSON_ERROR = -2; // the JSON relative error protected final int OTHER_ERROR = -3; // the unknow error /** * 将其它线程的数据转发到UI线程 */ private Handler mDeliveryHandler; private DisposeDataListener mListener; private Class<?> mClass; public CommonJsonCallback(DisposeDataHandle handle) { this.mListener = handle.mListener; this.mClass = handle.mClass; this.mDeliveryHandler = new Handler(Looper.getMainLooper()); } @Override public void onFailure(final Call call, final IOException ioexception) { /** * 此时还在非UI线程,因此要转发 */ mDeliveryHandler.post(new Runnable() { @Override public void run() { mListener.onFailure(new OkHttpException(NETWORK_ERROR, ioexception)); } }); } @Override public void onResponse(final Call call, final Response response) throws IOException { final String result = response.body().string(); final ArrayList<String> cookieLists = handleCookie(response.headers()); mDeliveryHandler.post(new Runnable() { @Override public void run() { handleResponse(result); /** * handle the cookie */ if (mListener instanceof DisposeHandleCookieListener) { ((DisposeHandleCookieListener) mListener).onCookie(cookieLists); } } }); } private ArrayList<String> handleCookie(Headers headers) { ArrayList<String> tempList = new ArrayList<String>(); for (int i = 0; i < headers.size(); i++) { if (headers.name(i).equalsIgnoreCase(COOKIE_STORE)) { tempList.add(headers.value(i)); } } return tempList; } private void handleResponse(Object responseObj) { if (responseObj == null || responseObj.toString().trim().equals("")) { mListener.onFailure(new OkHttpException(NETWORK_ERROR, EMPTY_MSG)); return; } try { /** * 协议确定后看这里如何修改 */ // JSONObject result = new JSONObject(responseObj.toString()); String result=responseObj.toString(); if (mClass == null) { //发送JSONObject类型 // mListener.onSuccess(result); // 发送json字符串 mListener.onSuccess(result); } else { /*--将json转化成实体类--*/ Object obj = JsonUtils.fromJson(result.toString(), mClass); if (obj != null) { mListener.onSuccess(obj); } else { mListener.onFailure(new OkHttpException(JSON_ERROR, EMPTY_MSG)); } } } catch (Exception e) { mListener.onFailure(new OkHttpException(OTHER_ERROR, e.getMessage())); e.printStackTrace(); } } }
CommonJsonCallback的构造方法传入的有DisposeDataHandle,
DisposeDataHandle
public class DisposeDataHandle { public DisposeDataListener mListener = null; public Class<?> mClass = null; public String mSource = null; public DisposeDataHandle(DisposeDataListener listener) { this.mListener = listener; } public DisposeDataHandle(DisposeDataListener listener, Class<?> clazz) { this.mListener = listener; this.mClass = clazz; } public DisposeDataHandle(DisposeDataListener listener, String source) { this.mListener = listener; this.mSource = source; } }
DisposeDataHandle需要传入具体的请求回调 ,也就是DisposeDataListener
DisposeDataListener
public interface DisposeDataListener<T> { /** * 请求成功回调事件处理 */ public void onSuccess(T t); /** * 请求失败回调事件处理 */ public void onFailure(Object reasonObj); }
封装到此告一段落,接下来看如何使用了。。
如果需要将json转化成实体类对象,需要给DisposeDataListener指定泛型,并且在最后还要传入DataBean.class:CommonOkhttpClient.sendRequest(CommonRequest.createGetRequest(UrlConstant.wxUrl,null), new CommonJsonCallback(new DisposeDataHandle(new DisposeDataListener<DataBean>() { @Override public void onSuccess(DataBean bean) { mTextView.setText("onSuccess:"+bean.getAds().toString()); } @Override public void onFailure(Object reasonObj) { mTextView.setText("onFailure:"+reasonObj.toString()); } },DataBean.class)));
如果想直接返回json字符串,需要给DisposeDataListener指定泛型为String即可
CommonOkhttpClient.sendRequest(CommonRequest.createGetRequest(UrlConstant.wxUrl,null), new CommonJsonCallback(new DisposeDataHandle(new DisposeDataListener<String>() { @Override public void onSuccess(String json) { mTextView.setText("onSuccess:"+json); } @Override public void onFailure(Object reasonObj) { mTextView.setText("onFailure:"+reasonObj.toString()); } })));
这样写的好处在于,单纯从这段代码来看,并没有暴露出我们具体使用的是okhttp还是其他网络框架,也就是说,如果以后要更换其他网络请求框架的话,应用层的代码就不用改变了。
其实这样看起来还不够好,在具体项目中,我会创建一个RequestManager来管理这些请求,真正达到一行代码实现发送请求
/** * Created by gechuanguang on 2017/2/22. * 邮箱:1944633835@qq.com * @Function 来管理一些网络请求 */ public class RequestManager { //根据参数发送所有get请求 private static void getRequest(String url, RequestParams params, DisposeDataListener<?> listener, Class<?> clazz) { CommonOkhttpClient.sendRequest(CommonRequest.createGetRequest(url,params), new CommonJsonCallback(new DisposeDataHandle(listener,clazz))); } //根据参数发送所有post请求 private static void postRequest(String url, RequestParams params, DisposeDataListener listener, Class<?> clazz) { CommonOkhttpClient.sendRequest(CommonRequest.createPostRequest(url,params), new CommonJsonCallback(new DisposeDataHandle(listener,clazz))); } /** * 请求主页数据 * * @param listener */ public static void requestHomeData(Class clazz,DisposeDataListener<?> listener) { RequestManager.getRequest(UrlConstant.wxUrl,null,listener,clazz); } /** * Login * */ }
然后在其他地方 就可以这么去发送请求:
RequestManager.requestHomeData(DataBean.class, new DisposeDataListener<DataBean>() { @Override public void onSuccess(DataBean bean) { mTextView.setText(bean.getAds().toString()); } @Override public void onFailure(Object reasonObj) { mTextView.setText(reasonObj.toString()); } });
这样看起来更加简洁了,降低了代码之间的耦合的,在应用层只要关心是请求登录接口还是主页接口,以及回调成功或失败即可。
代码地址Github,如果大神有更好的封装思路请赐教,我打算将这个Library维护下去,扩展更多的东西
参考文章
http://blog.csdn.net/lmj623565791/article/details/47911083http://blog.csdn.net/lcq5211314123
相关文章推荐
- 关于okhttp的二次封装
- Android Okhttp3框架二次封装请求管理类
- OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
- OkHttp的基本使用以及二次封装
- okhttp的二次封装(基本使用)
- Okhttp框架的二次封装,post json格式的参数
- Okhttp二次封装
- Okhttp单例 二次封装
- okhttp二次封装
- okhttp简单的二次封装
- OkHttp的二次封装
- OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
- okHttp二次封装
- OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
- OKHttp简单二次封装
- [置顶] Retrofit和OkHttp简单的二次封装
- okhttp的二次封装加入泛型
- Okhttp二次封装
- 二次封装oKHTTP
- okHttp的而二次封装,单例模式