在Android开发中网络框架的使用是必不可少的,okhttp相信大家一定不陌生,本篇基于okhttp进行二次封装,实现简单快捷的okhttp封装。
阅读前提:知道okhttp的简单使用。
主要包括四个部分:
参考:公司项目网络框架封装
这一部分主要包括:
/**
* 连接超时
*/
private static final long CONNECT_TIMEOUT_MILLIS = 15 * 1000;
/**
* 读取超时
*/
private static final long READ_TIMEOUT_MILLIS = 15 * 1000;
/**
* 写入超时
*/
private static final long WRITE_TIMEOUT_MILLIS = 15 * 1000;
// 同步请求超时
private static final long SYNC_TIMEOUT_MILLIS = 15 * 1000;
/**
* OkHttpClient实例
*/
private static OkHttpClient client; //异步
private static OkHttpClient syncClient; //同步
// 获取客户端实例
public static OkHttpClient getClient(){
// TODO: 2019/5/3 重定向
if (client == null){
client = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.addInterceptor(new com.hjl.commonlib.network.interceptor.LogInterceptor())
.readTimeout(READ_TIMEOUT_MILLIS,TimeUnit.MILLISECONDS)
.writeTimeout(WRITE_TIMEOUT_MILLIS,TimeUnit.MILLISECONDS)
.build();
}
return client;
}
// 同步获取客户端实例
private static OkHttpClient getSyncClient() {
if (syncClient == null) {
syncClient = new OkHttpClient.Builder()
.connectTimeout(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.addInterceptor(new com.hjl.commonlib.network.interceptor.LogInterceptor())
.addInterceptor(new RetryInterceptor())
.readTimeout(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.writeTimeout(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).build();
}
return syncClient;
}
日志拦截器
public class LogInterceptor implements Interceptor {
public static String TAG = "HTTP LogInterceptor";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
Response response = chain.proceed(request);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
MediaType mediaType = response.body().contentType();
String content = response.body().string();
Log.w(TAG,"----------Start----------------");
Log.i(TAG,"|" + request.toString());
String method = request.method();
if ("POST".equals(method)){
if (request.body() instanceof FormBody){
FormBody body = (FormBody) request.body();
for (int i = 0; i < body.size();i++){
// sb.append(body.encodedName(i) + " = " + body.encodedValue(i) + ",");
Log.i(TAG,"| RequestParams:{ params : " + body.encodedName(i) + " values: " + body.encodedValue(i) + " }");
}
}
}
Log.w(TAG, "Response: " );
Log.d(TAG,content);
Log.d(TAG,content);
Log.w(TAG,"----------End:" + duration + "毫秒----------");
return response.newBuilder().body(ResponseBody.create(mediaType,content)).build();
}
}
简单的封装了下
public class RequestParams {
private Map<String,String> params = new TreeMap<>();
private Map<String,File> fileMap = new ConcurrentHashMap<>();
public RequestParams() {
}
public RequestParams(Map<String,String> source) {
if (source != null){
params.putAll(source);
}
}
public void add(String key,String value){
params.put(key,value);
}
public void add(String key,File file){
fileMap.put(key,file);
}
public boolean hasParams(){
if (fileMap.size() > 0 || params.size() > 0){
return true;
}
return false;
}
public Map<String, File> getFileMap() {
return fileMap;
}
public Map<String, String> getParams() {
return params;
}
}
主要分为三类:Get、Post、文件上传:
public class CommonRequest {
private static final String TAG = "CommonRequest";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private static final MediaType MEDIA_TYPE_AUDIO = MediaType.parse("audio/*");
private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
/**
* post 请求
*/
public static Request createPostRequest(String url, RequestParams params){
return createPostRequest(url,params,null);
}
public static Request createPostRequest(String url,RequestParams params,RequestParams headers){
FormBody.Builder mFromBodyBuider = new FormBody.Builder();
//添加参数 并打印
if (params != null){
Log.w(TAG,"============== params list ==================");
for (Map.Entry<String,String> entry : params.getParams().entrySet()){
mFromBodyBuider.add(entry.getKey(),entry.getValue());
Log.w(TAG," params: " + entry.getKey() + " valus: " + entry.getValue());
}
Log.w(TAG,"============== params list ==================");
}
FormBody mFormBody = mFromBodyBuider.build();
Request.Builder request = new Request.Builder().url(url)
.post(mFormBody);
// 加入请求头
if (headers != null){
Headers.Builder mHeadBuilder = new Headers.Builder();
for (Map.Entry<String,String> entry : headers.getParams().entrySet()){
mHeadBuilder.add(entry.getKey(),entry.getValue());
}
request.headers(mHeadBuilder.build());
}
return request.build();
}
public static Request createPostRequest(String url,String json){
RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Log.w(TAG,"==================JSON params is:\n " + json);
return request;
}
/**
* get 请求
*/
public static Request getRequest(String url, Object tag) {
// LogUtils.i(TAG, "Request:\n" + url + "\n" + paramsJson);
Request request = new Request.Builder().url(url).get().tag(tag).build();
return request;
}
public static Request createGetRequest(String url , RequestParams params){
return createGetRequest(url,params,null);
}
public static Request createGetRequest(String url , RequestParams params ,RequestParams headers){
return createGetRequest(url,params,headers,null);
}
public static Request createGetRequest(String url , RequestParams params ,RequestParams headers,String tag){
StringBuilder urlBuilder = new StringBuilder(url).append("?");
if (params != null) {
for (Map.Entry<String, String> entry : params.getParams() .entrySet()) {
urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
Log.w(TAG,"params: " + entry.getKey() + " valus: " + entry.getValue());
}
}
Request.Builder request = new Request.Builder()
.url(urlBuilder.substring(0,urlBuilder.length()-1))
.tag(tag)
.get();
if (headers != null){
Headers.Builder mHeaderBuilder = new Headers.Builder();
for (Map.Entry<String,String> entry : headers.getParams().entrySet()){
mHeaderBuilder.add(entry.getKey(),entry.getValue());
}
request.headers(mHeaderBuilder.build());
}
return request.build();
}
/**
* 文件上传
*/
public static Request createFileRequest(String url,RequestParams params){
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
RequestBody requestBody ;
Map<String, File> map = params.getFileMap();
//遍历map中所有参数到builder
for (String key : map.keySet()) {
String fileType = getMimeType(map.get(key).getName());
Log.w(TAG,"file name: " + map.get(key).getName());
builder.addFormDataPart("files", map.get(key).getName(), RequestBody.create(MediaType.parse(fileType), map.get(key)));
}
requestBody = builder.build();
Request request = new Request.Builder()
.url(url).post(requestBody)//添加请求体
.build();
return request;
}
// 根据文件名字获取文件的mime类型
private static String getMimeType(String filename) {
FileNameMap filenameMap = URLConnection.getFileNameMap();
String contentType = filenameMap.getContentTypeFor(filename);
if (contentType == null) {
contentType = "application/octet-stream"; //* exe,所有的可执行程序
}
return contentType;
}
}
结合fastjson进行解析,将请求的结果解析成相应的对象,并且进行拆包,最终回调到相应方法
返回结果示例:其中result是数据
{
"state":true,
"flag":null,
"message":null,
"result":{
...
},
"errorCode":200
}
准备工作:
准备请求响应“外壳”类:
public class ServerTip {
private boolean state;
public int errorCode;
private String flag;
public String message;
// 省略构造方法和get、set方法
...
}
// 基本的类型
public class BaseResult extends ServerTip {
public BaseResult() {
}
public String result;
}
开始对结果进行处理:
可以结合实际情况对错误码进行封装,这里不做处理。
// T 为拆壳后的bean类
public abstract class HttpHandler<T> implements Callback {
protected Class<T> entityClass; //T.class 泛型的class类型 用于fastjson解析
protected Context mAppContext;
private static final String TAG = HttpHandler.class.getSimpleName();
public HttpHandler() {
this.mAppContext = MainApplication.get();
try {
// 获取泛型T 的类型
entityClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
} catch (Exception e) {
e.printStackTrace();
entityClass = (Class<T>) Object.class;
}
}
@Override
public final void onFailure(Call call, final IOException e) {
Log.d(TAG, "onFailure " + e.toString());
if (e instanceof UnknownHostException || e instanceof SocketException) {
onFailureOnUIThread(new ServerTip(300, "网络连接失败", false, ""));
} else {
onFailureOnUIThread(new ServerTip(301, "网络连接失败", false, ""));
}
}
@Override
public final void onResponse(Call call, Response response) throws IOException {
if (response.code() == 200) {
//请求码成功
String respBodyStr = response.body().string();
final String httpUrl = response.request().url().toString();
Headers headers = response.request().headers();
Log.w(TAG, "respBodyStr " + httpUrl + "\r\n header:" + headers + "\r\n");
Log.w(TAG, "respBodyStr result=:" + respBodyStr);
if (!StringUtils.isEmpty(respBodyStr)) {
// 解析数据
parseResult(respBodyStr);
} else {
onFailureOnUIThread(new ServerTip(301,"网络连接失败", false, ""));
}
} else {
onFailureOnUIThread(new ServerTip(301,"网络连接失败", false, ""));
}
}
protected void parseResult(String respBodyStr) {
try {
// 先解析为外壳的包装对象
BaseResult resp = JsonUtils.parseObject(respBodyStr, BaseResult.class);
if (resp != null) {
if (resp.errorCode() == 200) {
//请求成功
//后台没有返回data类型
if (resp.result == null) {
onSuccessOnUIThread(resp, null);
} else {
// 拆壳 解析结果类型
T data = JsonUtils.parseObject(resp.result, entityClass);
if (data != null) {
onSuccessOnUIThread(resp, data);
} else {
onFailureOnUIThread(new ServerTip(302, "JSON解析错误", false, ""));
}
}
} else {
onFailureOnUIThread(resp);
}
} else {
onFailureOnUIThread(new ServerTip(302, "JSON解析错误", false, ""));
}
} catch (Exception e) {
e.printStackTrace();
onFailureOnUIThread(new ServerTip(302, "JSON解析错误", false, ""));
}
}
// 切换到主线程
private void onSuccessOnUIThread(final ServerTip serverTip, final T t) {
MainApplication.runUiThread(() -> {
onSuccess(serverTip, t);
});
}
// 切换到主线程
private void onFailureOnUIThread(final ServerTip serverTip) {
MainApplication.runUiThread(() -> {
// 直接回调到错误的方法,在这可以判断用户token是否过期 过期跳转到登录页面
onFailure(serverTip);
});
}
public abstract void onSuccess(ServerTip serverTip, T t);
// 统一错误回调
public void onFailure(ServerTip serverTip) {
// 简单的打印出来 在这可以根据错误类型选择是否 Toast 提示用户等操作
Log.e(TAG, "Code:" + serverTip.errorCode() + " Msg:" + serverTip.message());
}
}
经过前面四步的封装,已经可以很方便的发起网络请求了,下面是网络请求工具类HttpUtils(okhttpClient的封装也这)
首先是POST请求
/**
* 异步post请求 带请求头
*/
public static void post(String url, RequestParams params, RequestParams headers, HttpHandler httpHandler ){
Request request = CommonRequest.createPostRequest(url,params,headers);
getClient().newCall(request).enqueue(httpHandler);
}
/**
* 异步post请求 不带请求头
*/
public static void post(String url, RequestParams params,HttpHandler httpHandler ){
post(url,params,null,httpHandler);
}
/**
* 同步post请求
*
*/
public static String postSync(String url,RequestParams params){
return postSync(url,params,null);
}
public static String postSync(String url,RequestParams params,RequestParams headers) {
Request request = CommonRequest.createGetRequest(url, params,headers);
try {
Response response = getSyncClient().newCall(request).execute();
final String result = response.body().string();
return result;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Get请求
/**
* get 请求 不带请求头
*/
public static void get(String url,RequestParams params,HttpHandler httpHandler){
get(url,params,null,httpHandler);
}
public static void get(String url,HttpHandler handler){
Request request = CommonRequest.getRequest(url,null);
getClient().newCall(request).enqueue(handler);
}
public static void get(String url,HttpHandler handler,String tag){
Request request = CommonRequest.getRequest(url,tag);
getClient().newCall(request).enqueue(handler);
}
/**
* get 请求 带请求头
*/
public static void get(String url,RequestParams params,RequestParams headers,HttpHandler httpHandler){
Request request = CommonRequest.createGetRequest(url,params,headers);
getClient().newCall(request).enqueue(httpHandler);
}
/**
* get 请求 带请求头 带tag
*/
public static void get(String url,RequestParams params,RequestParams headers,HttpHandler httpHandler,String tag){
Request request = CommonRequest.createGetRequest(url,params,headers,tag);
getClient().newCall(request).enqueue(httpHandler);
}
/**
* GET request with synchronization
*/
public static String getSync(String url,RequestParams params){
return getSync(url,params,null);
}
public static String getSync(String url,RequestParams params,RequestParams headers){
try {
Request request = CommonRequest.createGetRequest(url,params,headers);
Response response = getSyncClient().newCall(request).execute();
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
文件上传
/**
* file upload 文件上传
*/
public static void uploadFile(String url,RequestParams params,HttpHandler httpHandler){
Request request = CommonRequest.createFileRequest(url,params);
getClient().newCall(request).enqueue(httpHandler);
}
取消网络请求
public static void cancelTag(Object tag)
{
if (client != null){
for (Call call : client.dispatcher().queuedCalls())
{
if (tag.equals(call.request().tag()))
{
call.cancel();
}
}
for (Call call : client.dispatcher().runningCalls())
{
if (tag.equals(call.request().tag()))
{
call.cancel();
}
}
}
if (syncClient != null){
for (Call call : syncClient.dispatcher().queuedCalls())
{
if (tag.equals(call.request().tag()))
{
call.cancel();
}
}
for (Call call : syncClient.dispatcher().runningCalls())
{
if (tag.equals(call.request().tag()))
{
call.cancel();
}
}
}
}
将网络请求都封装到一个NetWorkWrapper类中,方便统一管理
public class NetWorkWrapper {
// post 请求示例
public static void warning(String id, HttpHandler<String> httpHandler){
String path = "xxx";
RequestParams params = new RequestParams();
params.add("warningId",id);
Http.post(path,params,httpHandler);
}
// get 请求示例 完整路径
public static void getMsg(String groupCode,HttpHandler<String> httpHandler){
String path = "xxx" + groupCode;
Http.get(path,httpHandler);
}
// get 请求示例 拼接路径 : www.xxx?key1=value1&key2=value2
public static void getMsg(String groupCode,HttpHandler<String> httpHandler){
String path = "xxx" ;
RequestParams params = new RequestParams();
params.add("groupCode",groupCode);
Http.get(path,params,httpHandler);
}
// 文件上传 请求示例
public static void filesUpload(File files, HttpHandler<String> handler) {
String path = "xxx";
RequestParams params = new RequestParams();
params.add("files", files);
Http.uploadFile(path, params, handler);
}
}
使用:
NetWorkWrapper.filesUpload(file, new HttpHandler<String>() {
@Override
public void onSuccess(ServerTip serverTip, String s) {
Log.d("TAG", "onSuccess: " + s);
}
@Override
public void onFailure(ServerTip serverTip) {
super.onFailure(serverTip);
Log.d("TAG", "onFailure: " + serverTip.message);
}
});
参考打印日志:
2020-02-27 17:43:17.487 10835-10988/com.hjl.testmodule W/HTTP LogInterceptor: ----------Start----------------
2020-02-27 17:43:17.488 10835-10988/com.hjl.testmodule I/HTTP LogInterceptor: |Request{method=POST, url=xxx/filesUpload, tags={}}
2020-02-27 17:43:17.488 10835-10988/com.hjl.testmodule W/HTTP LogInterceptor: Response:
2020-02-27 17:43:17.488 10835-10988/com.hjl.testmodule D/HTTP LogInterceptor: {"state":true,"flag":null,"message":null,"result":{"url":"xxx/1582796586233.png"},"errorCode":200}
2020-02-27 17:43:17.488 10835-10988/com.hjl.testmodule D/HTTP LogInterceptor: {"state":true,"flag":null,"message":null,"result":{"url":"xxx/1582796586233.png"},"errorCode":200}
2020-02-27 17:43:17.489 10835-10988/com.hjl.testmodule W/HTTP LogInterceptor: ----------End:1546毫秒----------
2020-02-27 17:43:17.490 10835-10988/com.hjl.testmodule W/HttpHandler: respBodyStr xxx/filesUpload
header:
2020-02-27 17:43:17.491 10835-10988/com.hjl.testmodule W/HttpHandler: respBodyStr result=:{"state":true,"flag":null,"message":null,"result":{"url":"xxx.png"},"errorCode":200}
2020-02-27 17:43:17.619 10835-10835/com.hjl.testmodule D/TAG: onSuccess: {"url":"xxx.png"}