网络请求是开发中最基础的功能,框架原生API不便于复用。今天在这里分享慕课一位老师基于OkHttp封装的一个思路,希望对大家有帮助。
首先,我们看一下Okhttp的基本使用
发送异步GET请求
1、new OkHttpClient;
2、构造Request对象;
3、通过前两步中的对象构建Call对象;
4、通过Call.enqueue(Callback)方法来提交异步请求;
String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以不写
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: ");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: " + response.body().string());
}
});
一个简单的get请求需要使用这么多代码,还有post请求?文件上传下载等等?
所以在实际开发中,完全不会这么用,原因如下:
1、代码冗余,几乎不可复用
2、一旦底层API改动,所有上层的网络请求代码度需要改动
3、没有进行封装
所以我们必须对OKhttp进行封装,如何封装那?
封装思路:
主要分为3部分的封装:
1、Request的封装
- 请求参数
- url
- 请求对象
2、OKhttp核心封装
- 发送请求
- 配置相关参数
- HTTPS的支持
3、Callback的封装
- 处理回调函数
- 异常处理
- 转发消息到我们的UI线程
- 讲json转化为实体对象
Request的封装
封装所有的请求参数到hashmap中
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author vision
* 封装所有的请求参数到hashmap中
*/
public class RequestParams {
public ConcurrentHashMap<String, String> urlParams = new ConcurrentHashMap<String, String>();
public ConcurrentHashMap<String, Object> fileParams = new ConcurrentHashMap<String, Object>();
/**
* Constructs a new empty {@code RequestParams} instance.
*/
public RequestParams() {
this((Map<String, String>) null);
}
/**
* Constructs a new RequestParams instance containing the key/value string
* params from the specified map.
*
* @param source the source key/value string map to add.
*/
public RequestParams(Map<String, String> source) {
if (source != null) {
for (Map.Entry<String, String> entry : source.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
}
/**
* Constructs a new RequestParams instance and populate it with a single
* initial key/value string param.
*
* @param key the key name for the intial param.
* @param value the value string for the initial param.
*/
public RequestParams(final String key, final String value) {
this(new HashMap<String, String>() {
{
put(key, value);
}
});
}
/**
* Adds a key/value string pair to the request.
*
* @param key the key name for the new param.
* @param value the value string for the new param.
*/
public void put(String key, String value) {
if (key != null && value != null) {
urlParams.put(key, value);
}
}
public void put(String key, Object object) throws FileNotFoundException {
if (key != null) {
fileParams.put(key, object);
}
}
public boolean hasParams() {
if(urlParams.size() > 0 || fileParams.size() > 0){
return true;
}
return false;
}
生成request对象
import java.io.File;
import java.util.Map;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.Request;
import okhttp3.RequestBody;
/**
* @author vision
* @function build the request
* 生成request对象
*/
public class CommonRequest {
/**
* create the key-value Request
*
* @param url
* @param params
* @return
*/
public static Request createPostRequest(String url, RequestParams params) {
return createPostRequest(url, params, null);
}
/**可以带请求头的Post请求
* @param url
* @param params
* @param headers
* @return
*/
public static Request createPostRequest(String url, RequestParams params, RequestParams headers) {
//添加请求体
FormBody.Builder mFormBodyBuild = new FormBody.Builder();
if (params != null) {
for (Map.Entry<String, String> entry : params.urlParams.entrySet()) {
mFormBodyBuild.add(entry.getKey(), entry.getValue());
}
}
//添加请求头
Headers.Builder mHeaderBuild = new Headers.Builder();
if (headers != null) {
for (Map.Entry<String, String> entry : headers.urlParams.entrySet()) {
mHeaderBuild.add(entry.getKey(), entry.getValue());
}
}
FormBody mFormBody = mFormBodyBuild.build();
Headers mHeader = mHeaderBuild.build();
Request request = new Request.Builder().url(url).
post(mFormBody).
headers(mHeader)
.build();
return request;
}
/**
* ressemble the params to the url
*
* @param url
* @param params
* @return
*/
public static Request createGetRequest(String url, RequestParams params) {
return createGetRequest(url, params, null);
}
/**
* 可以带请求头的Get请求
* @param url
* @param params
* @param headers
* @return
*/
public static Request createGetRequest(String url, RequestParams params, RequestParams headers) {
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("&");
}
}
//添加请求头
Headers.Builder mHeaderBuild = new Headers.Builder();
if (headers != null) {
for (Map.Entry<String, String> entry : headers.urlParams.entrySet()) {
mHeaderBuild.add(entry.getKey(), entry.getValue());
}
}
Headers mHeader = mHeaderBuild.build();
return new Request.Builder().
url(urlBuilder.substring(0, urlBuilder.length() - 1))
.get()
.headers(mHeader)
.build();
}
/**
* @param url
* @param params
* @return
*/
public static Request createMonitorRequest(String url, RequestParams params) {
StringBuilder urlBuilder = new StringBuilder(url).append("&");
if (params != null && params.hasParams()) {
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();
}
}
OKhttp核心封装
import com.youdu.okhttp.cookie.SimpleCookieJar;
import com.youdu.okhttp.https.HttpsUtils;
import com.youdu.okhttp.listener.DisposeDataHandle;
import com.youdu.okhttp.response.CommonFileCallback;
import com.youdu.okhttp.response.CommonJsonCallback;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import okhttp3.Call;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* @author qndroid
* @function 用来发送get, post请求的工具类,包括设置一些请求的共用参数,https支持
*/
public class CommonOkHttpClient {
private static final int TIME_OUT = 30;
private static OkHttpClient mOkHttpClient;
//client 配置
static {
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
/**
* 为所有请求添加请求头,看个人需求
*/
okHttpClientBuilder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("User-Agent", "Imooc-Mobile") // 标明发送本次请求的客户端
.build();
return chain.proceed(request);
}
});
okHttpClientBuilder.cookieJar(new SimpleCookieJar());
//设置超时时间
okHttpClientBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS);
okHttpClientBuilder.readTimeout(TIME_OUT, TimeUnit.SECONDS);
okHttpClientBuilder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);
//支持重定向
okHttpClientBuilder.followRedirects(true);
//https支持
okHttpClientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
/**
* trust all the https point
*/
okHttpClientBuilder.sslSocketFactory(HttpsUtils.initSSLSocketFactory(), HttpsUtils.initTrustManager());
mOkHttpClient = okHttpClientBuilder.build();
}
public static OkHttpClient getOkHttpClient() {
return mOkHttpClient;
}
/**
* 通过构造好的Request,Callback去发送请求
*
* @param request
* @param callback
*/
public static Call get(Request request, DisposeDataHandle handle) {
Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonJsonCallback(handle));//CommonJsonCallback Callback回调
return call;
}
public static Call post(Request request, DisposeDataHandle handle) {
Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonJsonCallback(handle));
return call;
}
public static Call downloadFile(Request request, DisposeDataHandle handle) {
Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonFileCallback(handle));
return call;
}
Callback的封装
定义OkHttpException
public class OkHttpException extends Exception {
private static final long serialVersionUID = 1L;
/**
* the server return code
*/
private int ecode;
/**
* the server return error message
*/
private Object emsg;
public OkHttpException(int ecode, Object emsg) {
this.ecode = ecode;
this.emsg = emsg;
}
public int getEcode() {
return ecode;
}
public Object getEmsg() {
return emsg;
}
}
DisposeDataListener 回调事件处理
public interface DisposeDataListener {
/**
* 请求成功回调事件处理
*/
public void onSuccess(Object responseObj);
/**
* 请求失败回调事件处理
*/
public void onFailure(Object reasonObj);
}
DisposeDataListener 在一层封装 DisposeDataHandle
public class DisposeDataHandle
{
public DisposeDataListener mListener = null;
public Class<?> mClass = null;//json->object object.class 后面要解析的json实体类
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;
}
}
CommonJsonCallback的封装
import android.os.Handler;
import android.os.Looper;
import com.youdu.okhttp.exception.OkHttpException;
import com.youdu.okhttp.listener.DisposeDataHandle;
import com.youdu.okhttp.listener.DisposeDataListener;
import com.youdu.okhttp.listener.DisposeHandleCookieListener;
import com.youdu.adutil.ResponseEntityToModule;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.Response;
/**
* @author vision
* @function
*
* 1、异常
* 2、数据解析
* 3、数据转发
*
*/
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());
if (mClass == null) {
//不需要解析,回调到应用层
mListener.onSuccess(result);
} else {
//将json数据解析为java对象返回到应用层
//ResponseEntityToModule 自定义的json数据解析类,这里可以使用gson,fastjson等
Object obj = ResponseEntityToModule.parseJsonObjectToModule(result, mClass);
if (obj != null) {
mListener.onSuccess(obj);
} else {
//json不合法
mListener.onFailure(new OkHttpException(JSON_ERROR, EMPTY_MSG));
}
}
} catch (Exception e) {
//其他异常
mListener.onFailure(new OkHttpException(OTHER_ERROR, e.getMessage()));
e.printStackTrace();
}
}
}
其实到这块
OKhttp的底层已经封装完成了。
为了降低耦合,我们可以在应用层在封装一下
import com.youdu.module.course.BaseCourseModel;
import com.youdu.module.recommand.BaseRecommandModel;
import com.youdu.module.update.UpdateModel;
import com.youdu.module.user.User;
import com.youdu.okhttp.CommonOkHttpClient;
import com.youdu.okhttp.listener.DisposeDataHandle;
import com.youdu.okhttp.listener.DisposeDataListener;
import com.youdu.okhttp.listener.DisposeDownloadListener;
import com.youdu.okhttp.request.CommonRequest;
import com.youdu.okhttp.request.RequestParams;
/**
* @author: vision
* @function:
*/
public class RequestCenter {
//根据参数发送所有post请求
public static void postRequest(String url, RequestParams params, DisposeDataListener listener, Class<?> clazz) {
CommonOkHttpClient.get(CommonRequest.
createGetRequest(url, params), new DisposeDataHandle(listener, clazz));
}
/**
* 用户登陆请求
*
* @param listener
* @param userName
* @param passwd
*/
public static void login(String userName, String passwd, DisposeDataListener listener) {
RequestParams params = new RequestParams();
params.put("mb", userName);
params.put("pwd", passwd);
RequestCenter.postRequest(HttpConstants.LOGIN, params, listener, User.class);
}
/**
* 应用版本号请求
*
* @param listener
*/
public static void checkVersion(DisposeDataListener listener) {
RequestCenter.postRequest(HttpConstants.CHECK_UPDATE, null, listener, UpdateModel.class);
}
public static void requestRecommandData(DisposeDataListener listener) {
RequestCenter.postRequest(HttpConstants.HOME_RECOMMAND, null, listener, BaseRecommandModel.class);
}
public static void downloadFile(String url, String path, DisposeDownloadListener listener) {
CommonOkHttpClient.downloadFile(CommonRequest.createGetRequest(url, null),
new DisposeDataHandle(listener, path));
}
/**
* 请求课程详情
*
* @param listener
*/
public static void requestCourseDetail(String courseId, DisposeDataListener listener) {
RequestParams params = new RequestParams();
params.put("courseId", courseId);
RequestCenter.postRequest(HttpConstants.COURSE_DETAIL, params, listener, BaseCourseModel.class);
}
}
http地址:
public class HttpConstants {
private static final String ROOT_URL = "http://imooc.com/api";
/**
* 请求本地产品列表
*/
public static String PRODUCT_LIST = ROOT_URL + "/fund/search.php";
/**
* 本地产品列表更新时间措请求
*/
public static String PRODUCT_LATESAT_UPDATE = ROOT_URL + "/fund/upsearch.php";
/**
* 登陆接口
*/
public static String LOGIN = ROOT_URL + "/user/login_phone.php";
/**
* 检查更新接口
*/
public static String CHECK_UPDATE = ROOT_URL + "/config/check_update.php";
/**
* 首页产品请求接口
*/
public static String HOME_RECOMMAND = ROOT_URL + "/product/home_recommand.php";
/**
* 课程详情接口
*/
public static String COURSE_DETAIL = ROOT_URL + "/product/course_detail.php";
}
具体的使用看一下:
//发送推荐产品请求
private void requestRecommandData() {
RequestCenter.requestRecommandData(new DisposeDataListener() {
@Override
public void onSuccess(Object responseObj) {
mRecommandData = (BaseRecommandModel) responseObj;
//更新UI
showSuccessView();
}
@Override
public void onFailure(Object reasonObj) {
//显示请求失败View
showErrorView();
}
});
}
到此,OkHttp3的封装就完成了