(输入参数)
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
id | Integer | 200 | Book ID |
(输出参数)
Response | 类型 | 默认值 | 描述 |
---|---|---|---|
code | Integer | 200 | 请求状态 |
message | String | 请求成功 | 请求信息 |
data | BookDetail | 请求二级协议 |
确定协议后首先确定的是 主机地址,然后确定具体的业务信息来构成 URL,一个推荐的方式是
下一步就是确定请求 request 报文:
而 response body 已经在上列的拖中展示出来,在获得 data 后这时候我们假设 data 字段是 json 格式的,然后将 json 转化成 Bean,也就是我们 java 中的实体类。
总结:为完成上述的要求也同时为了代码重用和避免代码混乱,我们需要一个通用的模式,下面就是一个简单却又完整的 BaseRequest 抽象类来完成上述要求。
public abstract class BaseRequest {
//存放 POST 请求主体内容, protected 关键字是为了给子类访问到。
protected HashMap mBodyMap = new HashMap();
//存放 GET 请求参数
protected HashMap mQueryMap = new HashMap();
private OkHttpClient mClient;
private Call mCall;
public BaseRequest(OkHttpClient httpClient) {
this.mClient = httpClient;
}
protected String getHost() {
return HttpConfig.HOST;
}
//api 地址
public abstract String getApi();
//请求方式
public abstract int getHttpMethod();
//返回 Bean
public abstract Class getModelClass();
//请求内容
public abstract MediaType getMediaType();
//将 mBodyMap 里面的 POST 请求体转成 String 格式传给 RequestBody.create() 方法
//在子类中完成,跟 getMediaType() 组合来完成不同业务的请求需求。
protected abstract String getRequestString();
//发出请求必须要有 URL
public String getUrl() {
if (HttpMethod.GET == getHttpMethod()) {
String queryString = getQueryString();
if (!TextUtils.isEmpty(queryString)) {
return String.format("%s%s?%s", getHost(), getApi(), queryString);
}
}
return String.format("%s%s", getHost(), getApi());
}
//将我们的常规请求转换为 okhttp 中的 request
protected Request buildRequest() {
RequestBody body = null;
Request.Builder builder = new Request.Builder();
String strRequest = getRequestString();
switch (getHttpMethod()) {
case HttpMethod.GET:
builder.url(getUrl());
break;
case HttpMethod.POST:
body = RequestBody.create(getMediaType(),strRequest);
builder.url(getUrl()).post(body);
break;
default:
//TODO
break;
}
return builder.build();
}
//返回 GET 请求参数集合,如 id=xxx&&name=xxx
public String getQueryString() {
if (mQueryMap != null && mQueryMap.size() > 0) {
return encodeParameters(mQueryMap, "utf-8");
}
return null;
}
//将 map 键值对取出来组装成 id=xxx&&name=xxx 格式
protected String encodeParameters(Map params, String paramsEncoding) {
try {
if (params != null && params.size() > 0) {
StringBuilder encodedParams = new StringBuilder();
int index = 0;
for (Map.Entry entry : params.entrySet()) {
if (TextUtils.isEmpty(entry.getKey()) || TextUtils.isEmpty(entry.getValue())) {
continue;
}
if (index > 0) encodedParams.append('&');
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
index++;
}
return encodedParams.toString();
} else {
return null;
}
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
}
public class HttpMethod {
public static final int POST = 1;
public static final int GET = 2;
}
public class HttpConfig {
public static final String HOST = "http://1.1.1.1";
}
public class HttpResponse {
public Integer code;
public String message;
public T data;
public HttpResponse(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public HttpResponse() {
}
}
/***
* 同步调用
*
* @return
* @throws Exception
*/
public HttpResponse execute() throws IOException {
Response response = this.mClient.newCall(buildRequest()).execute();
int code = response.code();
if (code == 200) {
//正常请求
ResponseBody body = response.body();
return getResponse(body);
} else {
return null;
}
}
//将服务器返回的 Json 数据解析成 HTTPResponse
private HttpResponse getResponse(ResponseBody body) throws IOException {
String strBody = body.string();
JSONObject json = JSON.parseObject(strBody);
HttpResponse httpResponse = new HttpResponse();
httpResponse.code = json.getInteger("code");
httpResponse.message = json.getString("message");
String strData = json.getString("data");
Object data = JSONObject.parseObject(strData, getModelClass());
httpResponse.data = (T) data;
return httpResponse;
}
//主线程分发器,因为 okhttp 的异步调用方法发生在子线程,我们需要将结果发送回主线程
private ExecutorDelivery mDelivery;
/***
* 异步调用
*/
public boolean enqueue(final HttpCallback callback) {
if (mCall == null) {
mCall = mClient.newCall(buildRequest());
mCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
HttpResponse response = new HttpResponse(ResponseCode.NET_ERROR, "网络错误", null);
mDelivery.postResponse(callback, BaseRequest.this, response, e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
HttpResponse httpResponse = null;
try {
httpResponse = getResponse(response.body());
mDelivery.postResponse(callback, BaseRequest.this, httpResponse, null);
} catch (IOException e) {
httpResponse = new HttpResponse(ResponseCode.NET_ERROR, "网络错误", null);
mDelivery.postResponse(callback, BaseRequest.this, httpResponse, e);
} catch (JSONException e) {
httpResponse = new HttpResponse(ResponseCode.JSON_ERROR,"Json解析错误", null);
mDelivery.postResponse(callback, BaseRequest.this, httpResponse, e);
}
}
});
} else {
if (mCall.isCanceled() || mCall.isExecuted()) {
Log.e("TAG", "cancel executed");
}
}
return true;
//请求回调接口
public interface HttpCallback {
/***
* 请求成功,httpcode 200,业务code == 200
*/
public void onResponse(BaseRequest request, Object data);
/**
* @param request
* @param e 异常:网络异常,json解析异常
* @param code 业务code,若httpcode为200,后两个参数有效;服务端的code,
* @param message 业务message
*/
public void onFailure(BaseRequest request, Exception e, int code, String message);
}
//注意下列 code 应该在一级协议中跟服务器约定好具体数值的含义!
public interface ResponseCode {
public static final int SUCCESS = 200;
public static final int FAILED = 2;
//其他内部错误
public static final int ERROR = -1;
public static final int NET_ERROR = -200;
/***
* json格式错误
*/
public static final int JSON_ERROR = -201;
}
//利用主线程创建的 Handler 来完成分发器的初始化工作。
public class ExecutorDelivery {
/**
* Used for posting responses, typically to the main thread.
*/
private final Executor mResponsePoster;
/**
* Creates a new response delivery interface.
*
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
public void postResponse(HttpCallback httpCallback, BaseRequest request, HttpResponse response, Exception e) {
mResponsePoster.execute(ResponseDeliveryRunnable2.response(httpCallback, request, response, e));
}
private static class ResponseDeliveryRunnable2 implements Runnable {
private final BaseRequest request;
private final Exception e;
private HttpResponse httpResponse;
private HttpCallback callback;
private ResponseDeliveryRunnable2(HttpCallback callback, BaseRequest request, HttpResponse response, Exception e) {
this.callback = callback;
this.request = request;
this.e = null;
this.httpResponse = response;
}
public static ExecutorDelivery.ResponseDeliveryRunnable2 response(HttpCallback modelCallback, BaseRequest request, HttpResponse response, Exception e) {
return new ExecutorDelivery.ResponseDeliveryRunnable2(modelCallback, request, response, e);
}
@SuppressWarnings("unchecked")
@Override
public void run() {
if (e == null && httpResponse.code == ResponseCode.SUCCESS) {
this.callback.onResponse(request, httpResponse.data);
} else {
this.callback.onFailure(request, e, httpResponse.code, httpResponse.message);
}
}
}
}
public BaseRequest(HttpClientWrapper httpClient) {
this.mClient = httpClient.getOkHttpClient();
this.mDelivery = httpClient.getDelivery();
}
//写这个网络封装器的目的是将业务层和底层网络库解耦,上层业务开发者不用关心用的是哪个网络库。
//在 Application 中初始化
public class HttpClientWrapper {
private OkHttpClient mOkHttpClient;
private ExecutorDelivery mDelivery;
public HttpClientWrapper(Context context) {
Handler handler = new Handler(Looper.getMainLooper());
mDelivery = new ExecutorDelivery(handler);
mOkHttpClient = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS).readTimeout(3, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS).build();
}
public OkHttpClient getOkHttpClient() {
return mOkHttpClient;
}
public ExecutorDelivery getDelivery() {
return mDelivery;
}
}