工具Android开发经验谈Android开发

最清晰的 OKhttp 封装框架步骤

2017-08-13  本文已影响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
上一篇 下一篇

猜你喜欢

热点阅读