手把手教你封装一个好用的okhttp请求框架

手把手教你封装一个好用的okhttp请求框架

      • 前言
      • OkHttpClient 客户端
      • 统一请求参数的封装
      • Request封装
      • 请求结果 & 错误解析
      • 网络请求的封装
      • 使用实例

前言

在Android开发中网络框架的使用是必不可少的,okhttp相信大家一定不陌生,本篇基于okhttp进行二次封装,实现简单快捷的okhttp封装。
阅读前提:知道okhttp的简单使用。
主要包括四个部分:

  • 请求client
  • 请求参数
  • Request封装
  • 请求结果 & 错误解析

参考:公司项目网络框架封装

OkHttpClient 客户端

这一部分主要包括:

  • 通用拦截器的加入(日志、通用请求头)
  • 请求超时时长

    /**
     * 连接超时
     */
    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;
    }

}

Request封装

主要分为三类: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"}

你可能感兴趣的:(Android,知识点总结)