美文网首页工具Android开发经验谈Android开发
最清晰的 OKhttp 封装框架步骤

最清晰的 OKhttp 封装框架步骤

作者: 菜鸟窝 | 来源:发表于2017-08-13 15:47 被阅读214次
    微信截图_20170809163734.png

    封装 OkHttp 框架,主要功能有:
    GET 请求
    POST FORM
    POST JSON

    封装之前,再次熟悉一下 OkHttp 请求网络的大致步骤,让封装的时候思路清晰一点。

    //1、初始化 OkHttpClient 对象
    private OkHttpClient mHttpClient = new OkHttpClient();
    //2、构造Request
    //2.1 构造RequestBody
    FormEncodingBuilder builder = new FormEncodingBuilder();
    RequestBody requestBody = builder.add("key", "value").build();
    final Request request = new Request
                .Builder()
                .post(requestBody)
                .url(url)
                .build();
    //3、将 Request 封装成 call
    final Call call = mHttpClient.newCall(request);
    //4、执行 call
    call.enqueue(new Callback() {
         //请求失败调用
         @Override
         public void onFailure(Request request, IOException e) {
          }
         //请求成功调用
          @Override
          public void onResponse(Response response) throws IOException {
          }
    });
    

    OkHttp 请求基本步骤就是这几步,看起来很简单,要封装自然也要考虑到这里面的方方面面,根据自己的业务需求进行相应的封装。封装是基于鸿洋大神的 OkHttpUtils,站在巨人的肩膀上,我们封装起来会更加简单。

    1、OkHttpClient 实例化

    声明一个私有的 OkHttpClient 对象,对其进行构建并设置超时信息,而 AsyncHttp 类则是我们要进行网络访问对外公开的类,OkHttpClient 官方建议我们设置成单例模式。

    public class AsyncHttp {
        private static AsyncHttp mInstance;
        //初始化操作,设置超时时间
        private OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(20 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(20 * 1000, TimeUnit.MILLISECONDS)
                .build();
        //私有构造函数,初始化 OkHttpClient 对象
        private AsyncHttp() {
            OkHttpUtils.initClient(okHttpClient);
        }
        //单例模式
        public static AsyncHttp instance() {
            if (mInstance == null) {
                synchronized (OkHttpUtils.class) {
                    if (mInstance == null) {
                        mInstance = new AsyncHttp();
                    }
                }
            }
            return mInstance;
        }
    }
    

    2、构造 GET 请求

    get 请求官方给我们的是没有参数的,不满足我们需求,根据我们的需求将其进行封装。GET 请求需要 Request 对象传参,还需要回调 CallBack,因此需要添加 Request 基类和 CallBack 基类。

    public void get(IRequest request, IHttpListener listener) {}
    

    IRequest 是 Request 的封装类,IHttpListener 是回调 CallBack 的监听。

    2.1、封装 IRequest

    我们知道官方提供的 Request 类是对请求信息进行封装,包括请求地址 url,请求方法 method,请求头 head,请求体 RequestBody,取消 http 请求的标志 tag。IRequest 是一个抽象类,请求信息封装在 RequestParams 类里面,IRequest 对外提供了一些方法,获取请求参数,获取/设置唯一标识,获取 Url,获取解析的数据类型。

    //IDontObfuscate 实现 Serializable 的一个抽象类
    public abstract class IRequest extends IDontObfuscate { 
        //测试登录的 API
        public static final String HOST_PUBLIC = "http://live.demo.cniao5.com/Api/";
        public static final String HOST_DEBUG = "http://192.168.31.92:8094/Api/";
        
        //判断上面两个 API 使用哪一个。表示猜不透直播老师的心啊,不知道为什么要这样
        private boolean DEBUG = false;
        //Request 请求信息封装类
        protected RequestParams mParams = new RequestParams();
        //请求网络直播登录的唯一标识
        public int mRequestId = 0;
        protected int mDraw = 0;
      
        public IRequest() {
        }
    
        /**
         * 接口请求参数
         */
        public RequestParams getParams() {
            return mParams;
        }
    
        /**
         * 设置接口请求唯一标识
         */
        public void setRequestId(int requestId) {
            mRequestId = requestId;
        }
    
        /**
         * 返回请求接口唯一标识
         */
        public int getRequestId() {
            return mRequestId;
        }
    
        /**
         * 当前接口的url地址
         */
        public abstract String getUrl();
    
        /**
         * 获取解析类型
         */
        public abstract Type getParserType();
    
        /**
         * 返回服务器接口地址
         */
        protected String getHost() {
            return DEBUG ? HOST_DEBUG : HOST_PUBLIC;
        }
    
        @Override
        public String toString() {
            return "IRequest [DEBUG=" + DEBUG
                    + ", mParams=" + mParams + ", mRequestId=" + mRequestId
                    + ", mDraw=" + mDraw + "]";
        }
        //是否缓存
        public boolean isCache() {
            return false;
        }
    }
    

    RequestParams 请求参数封装
    参数封装类,顾名思义就是对参数的保存和移除,该类适用于 GET 和 POST 参数,包括 url 参数封装,stream 参数封装,file 参数封装,file 数组参数封装,url 对象参数封装。全部都是存储在 ConcurrentHashMap 对象中,通过键值对进行存储,put 方法有很多重载,对不同形式的参数采取不同的添加方式。

    public class RequestParams implements Serializable{
        
        //二进制流数据
        public final static String APPLICATION_OCTET_STREAM = "application/octet-stream";
        //JSON数据格式    
        public final static String APPLICATION_JSON = "application/json";
        protected final static String LOG_TAG = "RequestParams";
        
        //存储 url 参数
        protected final ConcurrentHashMap<String, String> urlParams = new ConcurrentHashMap<String, String>();
        //存储 stream 参数
        protected final ConcurrentHashMap<String, StreamWrapper> streamParams = new ConcurrentHashMap<String, StreamWrapper>();
        //存储 file 参数
        protected final ConcurrentHashMap<String, FileWrapper> fileParams = new ConcurrentHashMap<String, FileWrapper>();
        //存储 fileArray 参数
        protected final ConcurrentHashMap<String, List<FileWrapper>> fileArrayParams = new ConcurrentHashMap<String, List<FileWrapper>>();
        //存储 url 对象参数
        protected final ConcurrentHashMap<String, Object> urlParamsWithObjects = new ConcurrentHashMap<String, Object>();
    
        //是否重复
        protected boolean isRepeatable;
        //标志 multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式
        protected boolean forceMultipartEntity = false;
        //用户 json 数据流
        protected boolean useJsonStreamer;
        //自动关闭输入流标志
        protected boolean autoCloseInputStreams;
        //保存上传有效载荷所需的时间
        protected String elapsedFieldInJsonStreamer = "_elapsed";
        //指定编码格式
        protected String contentEncoding = "utf-8";
        private Gson mGson = new Gson();
      
        /**
         * 构造一个空的 RequestParams 实例
         */
        public RequestParams() {
            this((Map<String, String>) null);
        }
    
        //获取 urlParams
        public String getUrlParams(String key){
            if(!TextUtils.isEmpty(key)&&urlParams.containsKey(key)){
                return urlParams.get(key);
            }
            return "";
        }
    
        /**
         * 构造一个新的RequestParams实例,该实例包含指定map中的键/值字符串参数。
         */
        public RequestParams(Map<String, String> source) {
            if (source != null) {
                for (Map.Entry<String, String> entry : source.entrySet()) {
                    put(entry.getKey(), entry.getValue());
                }
            }
        }
    
        /**
         * 构造一个新的RequestParams实例,并使用单个初始键/值填充
         */
        public RequestParams(final String key, final String value) {
            this(new HashMap<String, String>() {{
                put(key, value);
            }});
        }
    
        /**
         * 构造一个新的RequestParams实例,并用多个初始键/值填充
         */
        public RequestParams(Object... keysAndValues) {
            int len = keysAndValues.length;
            if (len % 2 != 0)
                throw new IllegalArgumentException("Supplied arguments must be even");
            for (int i = 0; i < len; i += 2) {
                String key = String.valueOf(keysAndValues[i]);
                String val = String.valueOf(keysAndValues[i + 1]);
                put(key, val);
            }
        }
    
    
        public void setContentEncoding(final String encoding) {
            if (encoding != null) {
                this.contentEncoding = encoding;
            } else {
                Log.d(LOG_TAG, "setContentEncoding called with null attribute");
            }
        }
    
        /**
         * 如果设置为true,即使没有文件或流被发送,也会将Content-Type头部强制为“multipart/form-data”
         * multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式
         */
        public void setForceMultipartEntityContentType(boolean force) {
            this.forceMultipartEntity = force;
        }
    
        /**
         * 添加 string 类型字符串到 urlParams
         */
        public void put(String key, String value) {
            if (key != null && value != null) {
                urlParams.put(key, value);
            }
        }
    
        /**
         * 将自定义提供的文件内容类型和文件名称添加到 fileArrayParams
         */
        public void put(String key, File files[]) throws FileNotFoundException {
            put(key, files, null, null);
        }
    
        /**
         * 将自定义提供的文件内容类型和文件名称添加到 fileArrayParams
         */
        public void put(String key, File files[], String contentType, String customFileName) throws FileNotFoundException {
    
            if (key != null) {
                List<FileWrapper> fileWrappers = new ArrayList<FileWrapper>();
                for (File file : files) {
                    if (file == null || !file.exists()) {
                        throw new FileNotFoundException();
                    }
                    fileWrappers.add(new FileWrapper(file, contentType, customFileName));
                }
                fileArrayParams.put(key, fileWrappers);
            }
        }
    
        /**
         * 将自定义提供的文件内容类型和文件名添加到 fileParams
         */
        public void put(String key, File file) throws FileNotFoundException {
            put(key, file, null, null);
        }
    
        /**
         * 将自定义提供的文件内容类型和文件名添加到 fileParams
         */
        public void put(String key, String customFileName, File file) throws FileNotFoundException {
            put(key, file, null, customFileName);
        }
    
        /**
         * 将自定义提供的文件内容类型和文件名添加到 fileParams
         */
        public void put(String key, File file, String contentType) throws FileNotFoundException {
            put(key, file, contentType, null);
        }
    
        /**
         * 将自定义提供的文件内容类型和文件名添加到 fileParams
         */
        public void put(String key, File file, String contentType, String customFileName) throws FileNotFoundException {
            if (file == null || !file.exists()) {
                throw new FileNotFoundException();
            }
            if (key != null) {
                fileParams.put(key, new FileWrapper(file, contentType, customFileName));
            }
        }
    
        /**
         * 添加 InputStream 流到 streamParams
         */
        public void put(String key, InputStream stream) {
            put(key, stream, null);
        }
    
        /**
         * 添加 InputStream 流到 streamParams
         */
        public void put(String key, InputStream stream, String name) {
            put(key, stream, name, null);
        }
    
        /**
         * 添加 InputStream 流到 streamParams
         */
        public void put(String key, InputStream stream, String name, String contentType) {
            put(key, stream, name, contentType, autoCloseInputStreams);
        }
    
        /**
         * 添加 InputStream 流到 streamParams
         */
        public void put(String key, InputStream stream, String name, String contentType, boolean autoClose) {
            if (key != null && stream != null) {
                streamParams.put(key, StreamWrapper.newInstance(stream, name, contentType, autoClose));
            }
        }
    
        /**
         * 添加 Object 对象到 urlParamsWithObjects
         */
        public void put(String key, Object value) {
            if (key != null && value != null) {
                urlParamsWithObjects.put(key, value);
            }
        }
    
        /**
         * 添加 int 类型字符串到 urlParams
         */
        public void put(String key, int value) {
            if (key != null) {
                urlParams.put(key, String.valueOf(value));
            }
        }
    
        /**
         * 添加 long 类型字符串到 urlParams
         */
        public void put(String key, long value) {
            if (key != null) {
                urlParams.put(key, String.valueOf(value));
            }
        }
    
        /**
         * 添加 String 类型字符串到 urlParamsWithObjects
         */
        public void add(String key, String value) {
            if (key != null && value != null) {
                Object params = urlParamsWithObjects.get(key);
                if (params == null) {
                    // 向后兼容,这将导致“k = v1&k = v2&k = v3”
                    params = new HashSet<String>();
                    this.put(key, params);
                }
                if (params instanceof List) {
                    ((List<Object>) params).add(value);
                } else if (params instanceof Set) {
                    ((Set<Object>) params).add(value);
                }
            }
        }
    
        /**
         * 从 request 中移除某个字段
         */
        public void remove(String key) {
            urlParams.remove(key);
            streamParams.remove(key);
            fileParams.remove(key);
            urlParamsWithObjects.remove(key);
            fileArrayParams.remove(key);
        }
    
        /**
         * 监测字段是否被定义
         */
        public boolean has(String key) {
            return urlParams.get(key) != null ||
                    streamParams.get(key) != null ||
                    fileParams.get(key) != null ||
                    urlParamsWithObjects.get(key) != null ||
                    fileArrayParams.get(key) != null;
        }
    
        public void setHttpEntityIsRepeatable(boolean flag) {
            this.isRepeatable = flag;
        }
    
        public void setUseJsonStreamer(boolean flag) {
            this.useJsonStreamer = flag;
        }
    
        /**
         * 通过流上传 JSON 对象时设置一个附加字段,以保存上载有效载荷所需的时间(以毫秒为单位)。 默认情况下,此字段设置为“_elapsed”。
         * 要禁用此功能,请将此方法调用为null作为字段值。
         */
        public void setElapsedFieldInJsonStreamer(String value) {
            this.elapsedFieldInJsonStreamer = value;
        }
    
        /**
         * 设置全局标志,用于确定在成功上传时是否自动关闭输入流。
         */
        public void setAutoCloseInputStreams(boolean flag) {
            autoCloseInputStreams = flag;
        }
    
        /**
         * http get builder 参数封装构造类
         */
        public GetBuilder getGetBuilder() {
            GetBuilder getBuilder = new GetBuilder();
            //添加参数 url 后面
            for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
                Log.i("log", "getBuilder value:" + entry.getValue());
                getBuilder.addParams(entry.getKey(), entry.getValue());
            }
            return getBuilder;
        }
    
        /**
         * post form 表单
         */
        public PostFormBuilder getPostFormBuilder() {
            PostFormBuilder postFormBuilder = new PostFormBuilder();
            // post 请求添加参数 url后面添加
            for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
                Log.i("log", "getBuilder value:" + entry.getValue());
                postFormBuilder.addParams(entry.getKey(), entry.getValue());
            }
            //post 请求添加参数  file 文件参数
            for (ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
                postFormBuilder.addFile(entry.getKey(), entry.getValue().file.getAbsolutePath(), entry.getValue().file);
            }
            //文件对象列表存储相关信息
            //public PostFormBuilder addFile(String name, String filename, File file)
            //{
            //  files.add(new FileInput(name, filename, file));
            //  return this;
    

    本文作者为菜鸟窝编辑蒋志碧

    添加菜鸟窝运营微信:yrioyou备注【菜鸟直播】入群学习

    微信图片_20170803172638.jpg

    关注菜鸟窝官网,免费领取“140套开源项目”

    微信图片_20170813151906.jpg 微信图片_20170813151919.jpg

    相关文章

      网友评论

        本文标题:最清晰的 OKhttp 封装框架步骤

        本文链接:https://www.haomeiwen.com/subject/lbkerxtx.html