Teach you to encapsulate a useful okhttp request framework
foreword
The use of network frameworks is essential in Android development. I believe everyone is familiar with okhttp. This article is based on okhttp for secondary packaging to achieve simple and fast okhttp packaging.
Reading prerequisite: know the simple use of okhttp.
It mainly includes four parts:
- request client
- request parameters
- Request encapsulation
- Request Result & Error Parsing
Reference: Encapsulation of company project network framework
OkHttpClient client
This part mainly includes:
- The addition of common interceptors (logs, common request headers)
- request timeout
/**
* 连接超时
*/
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;
}
log interceptor
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();
}
}
Encapsulation of unified request parameters
Simply encapsulated
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;
}
}
Request encapsulation
Mainly divided into three categories: Get, Post, and file upload:
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;
}
}
Request Result & Error Parsing
Parse with fastjson, parse the request result into the corresponding object, and unpack it, and finally call back to the corresponding method
Return result example: where result is data
{
"state":true,
"flag":null,
"message":null,
"result":{
...
},
"errorCode":200
}
Preparations:
Prepare request response "shell" class:
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;
}
Start to process the result:
the error code can be encapsulated according to the actual situation, and will not be processed here.
// 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());
}
}
Encapsulation of network requests
After the encapsulation of the previous four steps, the network request can be easily initiated. The following is the network request tool class HttpUtils (the encapsulation of okhttpClient is also here)
First is the POST request
/**
* 异步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 request
/**
* 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
/**
* file upload 文件上传
*/
public static void uploadFile(String url,RequestParams params,HttpHandler httpHandler){
Request request = CommonRequest.createFileRequest(url,params);
getClient().newCall(request).enqueue(httpHandler);
}
cancel network request
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();
}
}
}
}
use case
Encapsulate network requests into a NetWorkWrapper class to facilitate unified management
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);
}
}
use:
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);
}
});
Refer to the print log:
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"}