OKHttp的封装重构,用到了Builder模式、策略模式,泛型

一步一步走向泥潭,无法解脱

  • 前期项目刚启动的时候,可能项目比较赶,为了尽快做出功能,很多需要细分、封装、解耦合的代码和逻辑没有做到位,越到后期随着业务功能的增加,开发维护就会越困难
  • 比如刚开始的时候我们的项目就对接一个登录支付服务器,请求服务器传递的参数以及返回的数据都比较单一,一般都是post方法,媒体类型为json字符串,返回的json数据格式一般就code,msg,data三个字段,data字段根据业务返回不同数据类型
  • 后面为了数据安全,需要对数据进行加密解密,比如对post的字符串数据进行RSA,AES、DES或者自定义的加密算法进行加密,还需要对数据进行验签,防止篡改等
  • 后面还需要在网络请求中加入请求头header;加入拦截器Interceptor,增加数据压缩gzip等
  • 后面我们又新增了三个服务器,而且加密方式也不一样,有的加密有的不加密、有的需要验签有的不需要,返回的数据格式也不统一,这就太搞事情了,如果还在之前简单封装的网络请求代码中加功能,那后面的代码就太难维护
  • 之前封装的网络请求代码将数据解析、加解密、请求参数都耦合在一起的,封装的接口也有很多重载的,比如同步异步、请求方式、加密方式、header等,封装了好几个重载方法,一个大类几乎囊括了全部的对外接口和内部功能实现,if语句也是满天飞

下定决心,重新做人

  • 前期刚把项目架构进行了优化,终于有时间来优化下网络请求这一块的内容了
  • 网络重构这块其实还是比较麻烦的,由于网络这块的api是公共模块,提供给其他模块使用,调用之处就多达几十处,而且参数、方法也不一样,重构起来得特别的小心仔细,重构完成后都得去测试,只有测试才能发现你写的隐藏的漏洞,自己review代码可能会找不出问题,最好能有其他同事review你的代码
  • 既然是重构,那么我们就必须考虑多一点,把各种业务需求都考虑进去,尽量把功能业务细分、代码不冗余,可重用性高

开始实践吧

  • 本文涉及到的代码都只是基本的框架封装,具体业务逻辑并不涉及
  • 为了让网络框架能够尽可能的通用一些,我专门进行泛型话,后续好拓展
  • 网络请求的动作就连个一个是请求,一个是取消请求,所以我们定义一个ICallAdapter接口,里面就call()、cancel()连个方法,如下
    public interface ICallAdapter {
        void call();
    
        void cancel();
    }
    
  • 然后定义一个abstract 泛型实现类,实现ICallAdapter定义的接口,定义createOkHttpCall()、handleSuccessBody(ResponseBody responseBody)方法,如下
    package com.lcq.httplib.client.adapter;
    
    import com.lcq.httplib.ErrorEnum;
    
    import org.jetbrains.annotations.NotNull;
    
    import java.io.IOException;
    
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.Response;
    import okhttp3.ResponseBody;
    
    public abstract class AbsCallAdapter<T> implements ICallAdapter {
        protected final RequestParams<T> requestParams;
        protected final Call call;
    
        public AbsCallAdapter(RequestParams<T> requestParams) {
            this.requestParams = requestParams;
            this.call = createOkHttpCall();
        }
    
        public void call() {
            if (requestParams.isAsync()) {
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        callbackFail(ErrorEnum.NET_FAIL);
                    }
    
                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) {
                        handleResponse(response);
                    }
                });
            } else {
                try {
                    Response response = call.execute();
                    handleResponse(response);
                } catch (IOException e) {
                    e.printStackTrace();
                    callbackFail(ErrorEnum.NET_FAIL);
                }
            }
        }
    
        private void handleResponse(Response response) {
            if (!response.isSuccessful()) {
                requestParams.getCallback().onFail(response.code(), response.message());
                return;
            }
    
            ResponseBody responseBody = response.body();
            if (responseBody == null) {
                callbackFail(ErrorEnum.BODY_NULL);
                return;
            }
    
            handleSuccessBody(responseBody);
        }
    
        protected void callbackFail(ErrorEnum errorEnum) {
            requestParams.getCallback().onFail(errorEnum.getCode(), errorEnum.getMsg());
        }
    
        public void cancel() {
            call.cancel();
        }
    
        abstract protected Call createOkHttpCall();
    
        abstract protected void handleSuccessBody(ResponseBody responseBody);
    }
    

  • StringCallAdapter 继承AbsCallAdapter<String>,实现createOkHttpCall()和handleSuccessBody(ResponseBody responseBody)方法,如下
    package com.lcq.httplib.client.adapter;
    
    import android.text.TextUtils;
    
    import com.lcq.httplib.ErrorEnum;
    
    import java.io.IOException;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    import okhttp3.Call;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.RequestBody;
    import okhttp3.ResponseBody;
    
    public class StringCallAdapter extends AbsCallAdapter<String> {
        private static final OkHttpClient okHttpClient;
    
        static {
            okHttpClient = new OkHttpClient.Builder()
                    .readTimeout(9000, TimeUnit.MILLISECONDS)
                    .writeTimeout(9000, TimeUnit.MILLISECONDS)
                    .connectTimeout(9000, TimeUnit.MILLISECONDS)
                    .build();
        }
    
        public StringCallAdapter(RequestParams<String> requestParams) {
            super(requestParams);
        }
    
        @Override
        protected Call createOkHttpCall() {
            Request.Builder builder = new Request.Builder()
                    .url(requestParams.getUrl());
    
            RequestBody requestBody = requestParams.getRequestBody();
            if (requestBody != null) {
                builder.post(requestBody);
            }
    
            Map<String, String> headers = requestParams.getHeaders();
            if (headers != null && headers.size() > 0) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
                        continue;
                    }
                    builder.addHeader(key, value);
                }
            }
    
            return okHttpClient.newCall(builder.build());
        }
    
        @Override
        public void handleSuccessBody(ResponseBody responseBody) {
            try {
                String text = responseBody.string();
                requestParams.getCallback().onSuccess(text);
            } catch (IOException e) {
                e.printStackTrace();
                callbackFail(ErrorEnum.IO_EXCEPTION);
            }
        }
    }
    

  • RequestParams<T> 是封装的网络请求参数,包括url、headers、requestBody、async、callback,这是第一层参数的封装,使用了Builder模式,如下
    package com.lcq.httplib.client.adapter;
    
    import android.util.Log;
    
    import com.lcq.httplib.callback.Callback;
    
    import java.util.Map;
    
    import okhttp3.RequestBody;
    
    public class RequestParams<T> {
        private final String url;
        private final Map<String, String> headers;
        private final RequestBody requestBody;
        private final boolean async;
        private Callback<T> callback;
    
        private RequestParams(Builder<T> builder) {
            url = builder.url;
            headers = builder.headers;
            requestBody = builder.requestBody;
            async = builder.async;
            callback = builder.callBack;
        }
    
        public String getUrl() {
            return url;
        }
    
        public Map<String, String> getHeaders() {
            return headers;
        }
    
        public RequestBody getRequestBody() {
            return requestBody;
        }
    
    
        public boolean isAsync() {
            return async;
        }
    
        public Callback<T> getCallback() {
            if (callback == null) {
                callback = new Callback<T>() {
                    @Override
                    public void onSuccess(T data) {
                        Log.d("http", "onSuccess,no callback");
                    }
    
                    @Override
                    public void onFail(int code, String msg) {
                        Log.d("http", "onFail,no callback");
                    }
                };
            }
            return callback;
        }
    
        public static final class Builder<T> {
            private String url;
            private Map<String, String> headers;
            private RequestBody requestBody;
            private boolean async;
            private Callback<T> callBack;
    
            public Builder() {
            }
    
            public Builder<T> url(String val) {
                url = val;
                return this;
            }
    
            public Builder<T> headers(Map<String, String> val) {
                headers = val;
                return this;
            }
    
            public Builder<T> requestBody(RequestBody val) {
                requestBody = val;
                return this;
            }
    
            public Builder<T> async(boolean val) {
                async = val;
                return this;
            }
    
            public Builder<T> callBack(Callback<T> val) {
                callBack = val;
                return this;
            }
    
            public RequestParams<T> build() {
                return new RequestParams<>(this);
            }
        }
    }
    

  • 定义一个通用的携带泛型返回值类型Callback<T> 的回调类,如下
    package com.lcq.httplib.callback;
    
    public interface Callback<T> {
        void onSuccess(T data);
    
        void onFail(int code, String msg);
    }
    

  • 定义一个abstract 的泛型AbsClient<T>类,实现ICallAdapter,并定义连个成员变量,一个是ICallAdapter,一个是ClientParams<T>,代码如下
    package com.lcq.httplib.client;
    
    import com.lcq.httplib.client.adapter.ICallAdapter;
    
    public abstract class AbsClient<T> implements ICallAdapter {
        protected final ClientParams<T> clientParams;
        protected ICallAdapter callService;
    
        public AbsClient(ClientParams<T> clientParams) {
            this.clientParams = clientParams;
        }
    
        public void call() {
            if (callService == null) {
                callService = createCallService();
            }
            callService.call();
        }
    
        public void cancel() {
            if (callService == null) {
                return;
            }
    
            callService.cancel();
        }
    
        public ClientParams<T> getCallParams() {
            return clientParams;
        }
    
        protected abstract ICallAdapter createCallService();
    }
    

  • ClientParams<T> 封装了url、headers、postObject、post、async、callback,postObject是外部需要传递的数据对象,请求网络之前会根据解析器和加密器进行转化,callback的类型是DataCallback<T>,是结果回调,会根据解析器和加密器继续数据解析,返回泛型定义的类型,代码如下
    package com.lcq.httplib.client;
    
    import android.util.Log;
    
    import com.google.gson.Gson;
    import com.lcq.httplib.callback.DataCallback;
    
    import java.lang.reflect.Type;
    import java.util.Map;
    
    public class ClientParams<T> {
        private final String url;
        private final Map<String, String> headers;
        private final Object postObject;
        private final boolean post;
        private final boolean async;
        private DataCallback<T> callback;
    
        private ClientParams(Builder<T> builder) {
            url = builder.url;
            headers = builder.headers;
            postObject = builder.postObject;
            post = builder.post;
            async = builder.async;
            callback = builder.callback;
        }
    
        public String getUrl() {
            return url;
        }
    
        public Map<String, String> getHeaders() {
            return headers;
        }
    
        public Object getPostObject() {
            return postObject;
        }
    
        public boolean isPost() {
            return post;
        }
    
        public boolean isAsync() {
            return async;
        }
    
        public DataCallback<T> getCallback() {
            if (callback == null) {
                callback = new DataCallback<T>() {
                    @Override
                    public Type getDataType() {
                        return String.class;
                    }
    
                    @Override
                    public void onSuccess(T data) {
                        Log.d("http", "onSuccess,no DataCallback:");
                    }
    
                    @Override
                    public void onFail(int code, String msg) {
                        Log.d("http", "onFail,no DataCallback,code:" + code + ",msg:" + msg);
                    }
                };
            }
            return callback;
        }
    
        public static final class Builder<T> {
            private String url;
            private Map<String, String> headers;
            private Object postObject;
            private boolean post;
            private boolean async = true;//默认异步
            private DataCallback<T> callback;
    
            public Builder() {
            }
    
            public Builder<T> url(String val) {
                Log.d("http", "client.url:" + val);
                url = val;
                return this;
            }
    
            public Builder<T> headers(Map<String, String> val) {
                headers = val;
                return this;
            }
    
            public Builder<T> postObject(Object val) {
                Log.d("http", "client.postObject:" + (val instanceof String ? val : new Gson().toJson(val)));
                postObject = val;
                post = true;
                return this;
            }
    
            public Builder<T> async(boolean val) {
                async = val;
                return this;
            }
    
            public Builder<T> callback(DataCallback<T> val) {
                callback = val;
                return this;
            }
    
            public ClientParams<T> build() {
                return new ClientParams<>(this);
            }
        }
    }
    

  • DataCallback<T> 继承Callback<T> 接口,新定义了一个getDataType() 接口,用于获取期望的数据类型,如下
    package com.lcq.httplib.callback;
    
    import java.lang.reflect.Type;
    
    public interface DataCallback<T> extends Callback<T> {
        Type getDataType();
    }
    

  • 定义一个abstract StringClient<T>类继承AbsClient<T>,实现createCallService()接口,并定义三个虚方法postParamToText(Object param)、encryptor()、dataParser()。代码如下
    package com.lcq.httplib.client;
    
    import android.util.Log;
    
    import com.lcq.httplib.ErrorEnum;
    import com.lcq.httplib.client.adapter.ICallAdapter;
    import com.lcq.httplib.client.adapter.RequestParams;
    import com.lcq.httplib.client.adapter.StringCallAdapter;
    import com.lcq.httplib.callback.Callback;
    import com.lcq.httplib.callback.DataCallback;
    import com.lcq.httplib.encryptor.Encryptor;
    import com.lcq.httplib.parser.DataParser;
    
    import okhttp3.MediaType;
    import okhttp3.RequestBody;
    
    public abstract class StringClient<T> extends AbsClient<T> {
        protected final Encryptor encryptor;
        protected final DataParser<T> dataParser;
    
        public StringClient(ClientParams<T> clientParams) {
            super(clientParams);
            this.encryptor = encryptor();
            this.dataParser = dataParser();
        }
    
        @Override
        protected ICallAdapter createCallService() {
            RequestParams.Builder<String> builder = new RequestParams.Builder<String>()
                    .url(clientParams.getUrl())
                    .headers(clientParams.getHeaders())
                    .async(clientParams.isAsync())
                    .callBack(new Callback<String>() {
                        @Override
                        public void onSuccess(String data) {
                            DataCallback<T> callback = clientParams.getCallback();
                            if (dataParser == null) {
                                callback.onFail(ErrorEnum.DATA_PARSER_NULL.getCode(), ErrorEnum.DATA_PARSER_NULL.getMsg());
                                return;
                            }
    
                            if (encryptor != null) {
                                try {
                                    data = encryptor.decrypt(data);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    callback.onFail(ErrorEnum.DECRYPT_FAIL.getCode(), ErrorEnum.DECRYPT_FAIL.getMsg());
                                }
                            }
    
                            dataParser.parse(data, callback);
                        }
    
                        @Override
                        public void onFail(int code, String msg) {
                            Log.d("http", "code:" + code + ",msg:" + msg);
                            clientParams.getCallback().onFail(code, msg);
                        }
                    });
    
            if (clientParams.isPost()) {
                builder.requestBody(createRequestBody(clientParams.getPostObject()));
            }
    
            return new StringCallAdapter(builder.build());
        }
    
        private RequestBody createRequestBody(Object postParam) {
            String content = null;
            if (postParam == null) {
                Log.d("http", "postParam null");
            } else {
                content = postParamToText(postParam);
    
                if (encryptor != null) {
                    content = encryptor.encrypt(content);
                }
            }
    
            if (content == null) {
                Log.d("http", "post content is null,set empty String");
                content = "";
            }
    
            return RequestBody.create(content, MediaType.parse("application/json;charset=utf-8"));
        }
    
        protected abstract String postParamToText(Object param);
    
        protected abstract Encryptor encryptor();
    
        protected abstract DataParser<T> dataParser();
    }
    

  • 定义一个Encryptor接口,用于数请求数据和返回数据的加解密,如下
    package com.lcq.httplib.encryptor;
    
    public interface Encryptor {
        String encrypt(String content);
    
        String decrypt(String content);
    }
    

  •  定义一个DataParser<T>类,用于解析返回的数据,如下
    package com.lcq.httplib.parser;
    
    import com.lcq.httplib.callback.DataCallback;
    
    public interface DataParser<T> {
        void parse(String content, DataCallback<T> callback);
    }
    

  • 定义一个DefaultStringClient<T> 继承 StringClient<T> 类,这个类就可以对外使用了,其默认实现了postParam 转字符串数据,空加密器,CommonDataParser 通用data数据解析器,如下
    package com.lcq.httplib.client;
    
    import com.google.gson.Gson;
    import com.lcq.httplib.encryptor.Encryptor;
    import com.lcq.httplib.parser.CommonDataParser;
    import com.lcq.httplib.parser.DataParser;
    
    public class DefaultStringClient<T> extends StringClient<T> {
        public DefaultStringClient(ClientParams<T> clientParams) {
            super(clientParams);
        }
    
        @Override
        public String postParamToText(Object param) {
            if (param == null) {
                return null;
            }
    
            if (param instanceof String) {
                return param.toString();
            }
    
            return new Gson().toJson(param);
        }
    
        @Override
        protected Encryptor encryptor() {
            return null;
        }
    
        @Override
        public DataParser<T> dataParser() {
            return new CommonDataParser<>();
        }
    }
    

  • 后续我们有不同的服务器请求数据,加解密不一样,数据格式不一样,就继承StringClient<T>这个类就可以了,比如我们要请求天气api接口,就可以这样封装,定义一个WeatherClient类,继承StringClient<Map<String, Object>> ,代码如下
    package com.lcq.httplib.client;
    
    import com.google.gson.Gson;
    import com.lcq.httplib.encryptor.Encryptor;
    import com.lcq.httplib.parser.DataParser;
    import com.lcq.httplib.parser.WeatherParser;
    
    import java.util.Map;
    
    public class WeatherClient extends StringClient<Map<String, Object>> {
        public WeatherClient(ClientParams<Map<String, Object>> clientParams) {
            super(clientParams);
        }
    
        @Override
        protected String postParamToText(Object param) {
            if (param == null) return null;//自己根据服务端协议,将param转换为需要的post文本
            return new Gson().toJson(param);
        }
    
        @Override
        protected Encryptor encryptor() {
            return null;
        }
    
        @Override
        protected DataParser<Map<String, Object>> dataParser() {
            return new WeatherParser();
        }
    }
    

  • 在定义一个天气解析器WeatherParser,实现DataParser<Map<String, Object>> 接口,如下
    package com.lcq.httplib.parser;
    
    import com.google.gson.Gson;
    import com.lcq.httplib.callback.DataCallback;
    
    import java.util.Map;
    
    public class WeatherParser implements DataParser<Map<String, Object>> {
        @Override
        public void parse(String content, DataCallback<Map<String, Object>> callback) {
            WeatherInfo weatherInfo = new Gson().fromJson(content, WeatherInfo.class);
            callback.onSuccess(weatherInfo.getWeatherinfo());
        }
    }
    

  • 然后我们在项目里面进行调用,调用采用链式结构,看起来很简洁,如下
    package com.lcq.lcqhttp;
    
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.lcq.httplib.callback.MapCallback;
    import com.lcq.httplib.client.ClientParams;
    import com.lcq.httplib.client.WeatherClient;
    
    import java.util.Map;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void testClient(View view) {
            new WeatherClient(new ClientParams.Builder<Map<String, Object>>()
                    .url("http://www.weather.com.cn/data/cityinfo/101010100.html")
                    .callback(new MapCallback() {
                        @Override
                        public void onSuccess(Map<String, Object> data) {
                            StringBuilder sb = new StringBuilder("天气信息:");
                            for (Map.Entry<String, Object> entry : data.entrySet()) {
                                sb.append(entry.getKey()).append(" = ").append(entry.getValue()).append("\n");
                                Log.d("http", "entry:" + entry.getKey() + "=" + entry.getValue());
                            }
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(MainActivity.this, sb.toString(), Toast.LENGTH_LONG).show();
                                }
                            });
    
                        }
    
                        @Override
                        public void onFail(int code, String msg) {
                            Log.d("http", "onFail.code:" + code + "msg:" + msg);
                        }
                    })
                    .build())
                    .call();
        }
    }

    请求的返回数据也正常:

  • 如果是相同的服务器,请求的数据转化,加密,以及返回的数据的解密,解析基本是不会变化的,如果对一个新的服务器,就定义一个新的类,继承StringClient<T>,根据需要实现Encryptor和DataParser<T>接口就可以了
  • 如果需要下载一张图片,我们定义一个BitmapClient类,继承AbsClient<Bitmap> ,实现createCallService()接口,返回一个InputStreamCallAdapter对象,代码如下,BitmapClient:
    package com.lcq.httplib.client;
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    
    import com.lcq.httplib.client.adapter.ICallAdapter;
    import com.lcq.httplib.client.adapter.InputStreamCallAdapter;
    import com.lcq.httplib.client.adapter.RequestParams;
    import com.lcq.httplib.callback.Callback;
    
    import java.io.InputStream;
    
    public class BitmapClient extends AbsClient<Bitmap> {
        public BitmapClient(ClientParams<Bitmap> clientParams) {
            super(clientParams);
        }
    
        @Override
        protected ICallAdapter createCallService() {
    
            RequestParams.Builder<InputStream> builder = new RequestParams.Builder<InputStream>()
                    .url(clientParams.getUrl())
                    .async(clientParams.isAsync())
                    .headers(clientParams.getHeaders())
                    .callBack(new Callback<InputStream>() {
                        @Override
                        public void onSuccess(InputStream data) {
                            Bitmap bitmap = BitmapFactory.decodeStream(data);
                            clientParams.getCallback().onSuccess(bitmap);
                        }
    
                        @Override
                        public void onFail(int code, String msg) {
                            clientParams.getCallback().onFail(code, msg);
                        }
                    });
            return new InputStreamCallAdapter(builder.build());
        }
    }
    

    InputStreamCallAdapter:

    package com.lcq.httplib.client.adapter;
    
    import com.lcq.httplib.ErrorEnum;
    
    import java.io.InputStream;
    
    import okhttp3.Call;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.ResponseBody;
    
    public class InputStreamCallAdapter extends AbsCallAdapter<InputStream> {
        public InputStreamCallAdapter(RequestParams<InputStream> requestParams) {
            super(requestParams);
        }
    
        @Override
        public Call createOkHttpCall() {
            return new OkHttpClient().newCall(new Request.Builder()
                    .url(requestParams.getUrl())
                    .build());
        }
    
        @Override
        protected void handleSuccessBody(ResponseBody responseBody) {
            try {
                InputStream inputStream = responseBody.byteStream();
                requestParams.getCallback().onSuccess(inputStream);
                responseBody.close();
            } catch (Exception e) {
                requestParams.getCallback().onFail(ErrorEnum.PARSE_FAIL.getCode(), ErrorEnum.PARSE_FAIL.getMsg());
            }
        }
    }
    

  • 如果我们还有服务器有AES加密,返回数据格式和DefaultStringClient的一样,可以定义一个 AesClient<T> 继承 DefaultStringClient<T>,重写encryptor()就可以了,重用了DefaultStringClient的解析器CommonDataParser,代码如下:AesClient<T>
    package com.lcq.httplib.client;
    
    
    import com.lcq.httplib.encryptor.AesEncryptor;
    import com.lcq.httplib.encryptor.Encryptor;
    
    public class AesClient<T> extends DefaultStringClient<T> {
    
        public AesClient(ClientParams<T> clientParams) {
            super(clientParams);
        }
    
        @Override
        protected Encryptor encryptor() {
            return new AesEncryptor();
        }
    }
    

  • 如果后面有post上传文件的需求,也可以很容易的实现,读者可以自己考虑下怎么去实现
  • 题外话:如果是做SDK开发的,项目中不能有太多的第三方框架依赖,不然容易引起版本兼容问题,同一个依赖库的不同版本接口都会有不同的变化,好一点的三方库的api应该是兼容低版本的,这次开发过程中我就遇到一个问题,开发的时候用的是高版本的OKHTTP库,将自己的库发布到项目进行使用的时候,报了一个方法找不到的问题,原来是低版本的api没有RequestBody.create(String, MediaType) 这个方法,所以为了兼容,我只能采用RequestBody.create(MediaType,String )这个已被高版本废弃的方法
  • 如果你觉得这种封装方式还可以,不妨点个赞哦,哈哈哈哈.....
  • 需要参考我这种封装方式的可以访问这个地址去看源码:LcqHttp: 封装了网络请求OKHTTP的框架
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值