【从 0 开始开发一款直播 APP】4.3 网络封装之 OkHttp -- 封装 GET,POST FORM,POST JSON

本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
视频地址:http://www.cniao5.com/course/10121


【从 0 开始开发一款直播 APP】4.1 网络封装之 Okhttp -- 基础回顾
【从 0 开始开发一款直播 APP】4.2 网络封装之 OkHttp -- GET,POST,前后端交互
【从 0 开始开发一款直播 APP】4.3 网络封装之 OkHttp -- 封装 GET,POST FORM,POST JSON
【从 0 开始开发一款直播 APP】4.4 网络封装之 OkHttp -- 网络请求实现直播登录


封装 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;
        //}
        return postFormBuilder;
    }
    
    // post String 字符串
    public PostStringBuilder getPostJsonBuilder() {
        PostStringBuilder postFormBuilder = new PostStringBuilder();
        //将对象转成 json 字符串传递给 PostStringBuilder
        postFormBuilder.content(mGson.toJson(urlParams));
        return postFormBuilder;
    }

    //文件包装类
    public static class FileWrapper implements Serializable {
        public final File file;
        public final String contentType;
        public final String customFileName;

        public FileWrapper(File file, String contentType, String customFileName) {
            this.file = file;
            this.contentType = contentType;
            this.customFileName = customFileName;
        }
    }

    //输入流包装类  
    public static class StreamWrapper {
        public final InputStream inputStream;//输入流
        public final String name;//文件名
        public final String contentType;//contentType 媒体类型
        public final boolean autoClose;//是否自动关闭

        public StreamWrapper(InputStream inputStream, String name, String contentType, boolean autoClose) {
            this.inputStream = inputStream;
            this.name = name;
            this.contentType = contentType;
            this.autoClose = autoClose;
        }
        
        //输入流包装类  APPLICATION_OCTET_STREAM:二进制流数据标志
        static StreamWrapper newInstance(InputStream inputStream, String name, String contentType, boolean autoClose) {
            return new StreamWrapper(
                    inputStream,
                    name,
                    contentType == null ? APPLICATION_OCTET_STREAM : contentType,
                    autoClose);
        }
    }
}

2.2、IHttpListener 接口

在 OkHttp 的 CallBack 类中,封装了 onSuccess() 和 onFailure() 方法,我们按照类似需求进行添加相应的方法。
onStart() 方法用于网络请求之前的一些操作,onSuccess() 网络请求成功的回调,onFailure() 网络请求成功的回调。

public interface IHttpListener {
   //请求网络之前回调(对话框提示信息等)  requestId:网络请求唯一标识
   void onStart(int requestId);
   //请求网络成功回调  requestId:网络请求唯一标识,response:Response 对象
   void onSuccess(int requestId, Response response);
   //请求网络失败回调  requestId:网络请求唯一标识,httpStatus:状态码,error:错误信息
   void onFailure(int requestId, int httpStatus, Throwable error);
}

两个重要的参数算是封装完了,下面开始 GET 请求方法实现。

public void get(IRequest request, IHttpListener listener) {
   LogDebugUtil.e(TAG, "post: url=" + request.getUrl());
   if (request != null) {
     //请求参数构造,其实就是构造 Request 对象,Request 采用 build 模式,这里也一样
     //RequestParams params = request.getParams();
     //GetBuilder 来自 OkHttpUtils
     //GetBuilder getBuilder = params.getGetBuilder();
     //RequestCall 和 Call 对象类似,就是用来执行网络请求的
     //RequestCall call = getBuilder.url(request.getUrl())
     //         .id(request.getRequestId()).build();
     //执行 call,ResponseCallback是自定义 CallBack,listener:回调监听,request.getParserType():请求类型解析  ResponseCallback(IHttpListener httpListener, Type parserType)
     //call.execute(new ResponseCallback(listener,request.getParserType()));
     
      //链式调用,上面是分析调用过程
      request.getParams()
            .getGetBuilder()
            .url(request.getUrl())
            .id(request.getRequestId())
            .build()
            .execute(new ResponseCallback(listener, request.getParserType()));
   } else {
      throw new RuntimeException("Request param is null");
   }
}

3、CallBack 封装

看到 鸿洋大神的 github 上对于自定义 CallBack 写了示例,根据这个示例,我们来封装一个自己的 CallBack。



先定义一个 Bean,针对下面这个 json 串进行封装,这个是登录的请求信息。

{  "status": 0,  
     "msg": "请求成功",  
     "data": { 
      "id": "1",    "nickname": "pzf“
      }
}

创建一个 Response 对象,声明需要解析的数据

//IDontObfuscate 是一个实现了 Serializable 接口的序列化抽象类,上面已经讲过了
public class Response<T>  extends IDontObfuscate {
   public int status;//状态码
   public String msg;//状态信息
   public T data;//对象
   @Override
   public String toString() {
      return "Response [code=" + status + ", msg=" + msg + ", data=" + data
            + "]";
   }

}

ResponseCallback — 自定义 Callback

//ResponseCallback 继承自 OkHttpUtils 框架中的 Callback,Response 是一个 bean 对象,上面讲过
class ResponseCallback extends Callback<Response> {
   private IHttpListener mHttpListener;//callback 回调监听
   private Type mParserType;//数据转换类型
    
   public ResponseCallback(IHttpListener httpListener, Type parserType) {
      mHttpListener = httpListener;
      mParserType = parserType;
   }

  //请求网络之前回调
   @Override
   public void onBefore(Request request, int id) {
      if (mHttpListener != null) {
         mHttpListener.onStart(id);
      }
   }

   @Override
   public Response parseNetworkResponse(okhttp3.Response response, int id) throws Exception {
      LogDebugUtil.e(TAG, "parseNetworkResponse: ");
      Response responseData = null;
      if (mHttpListener != null && response != null) {
         if (response.isSuccessful()) {
            String content = response.body().string();
            if (content != null) {
               try {
                  LogDebugUtil.e(TAG, "onSuccess: " + content);
                  //解析数据,返回解析之后的数据
                  responseData = mGson.fromJson(content,mParserType);
               } catch (JsonSyntaxException e) {
                  onError(null,e,id);
               }
            }
         } else {
            onError(null, new Exception("net error"),id);
         }
      }
      return responseData;
   }

  //请求错误回调
   @Override
   public void onError(Call call, Exception e, int id) {
      if (mHttpListener != null) {
         if (!call.isCanceled()) {
            mHttpListener.onFailure(id, 0, e);
         }
      }
   }

  //请求成功回调
   @Override
   public void onResponse(Response response, int id) {
      if (mHttpListener != null) {
         mHttpListener.onSuccess(id, response);
      }
   }
}

OkHttpUtils 中 Callback 源码

public abstract class Callback<T>{
    /**
     * UI Thread 请求网络之前调用
     */
    public void onBefore(Request request, int id){
    }

    /**
     * UI Thread 请求网络之后调用
     */
    public void onAfter(int id){
    }

    /**
     * UI Thread 更新进度条
     */
    public void inProgress(float progress, long total , int id){
    }

    /**
     * 如果在 parseNetworkResponse 中解析响应代码,则应该使此方法返回 true。
     */
    public boolean validateReponse(Response response, int id)
    {
        return response.isSuccessful();
    }

    /**
     * Thread Pool Thread 根据示例可以看出,该方法是解析数据的,T 是数据源
     */
    public abstract T parseNetworkResponse(Response response, int id) throws Exception;
    public abstract void onError(Call call, Exception e, int id);
    public abstract void onResponse(T response, int id);

    public static Callback CALLBACK_DEFAULT = new Callback(){
        @Override
        public Object parseNetworkResponse(Response response, int id) throws Exception{
            return null;
        }

        @Override
        public void onError(Call call, Exception e, int id){
        }

        @Override
        public void onResponse(Object response, int id){
        }
    };
}

到此,GET 请求封装完成。

4、构造 POST 请求

post 请求这里封装两个,Form 表单和 Json 字符串的 Post 提交封装。

4.1、Post Form

post 和 get 类似,之前 RequestParams 请求参数封装类已经构造完成,这里可以直接使用。

public void postForm(IRequest request, IHttpListener listener) {
   LogDebugUtil.e(TAG, "postForm: url=" + request.getUrl());
   if (request != null) {
     //请求参数构造,其实就是构造 Request 对象,Request 采用 build 模式,这里也一样
     //RequestParams params = request.getParams();
     //PostFormBuilder 来自 OkhttpUtils
     //PostFormBuilder postFormBuilder = params.getPostFormBuilder();
     //RequestCall 和 Call 对象类似,就是用来执行网络请求的
     //RequestCall call = postFormBuilder.url(request.getUrl())
     //         .id(request.getRequestId()).build();
     //执行 call,ResponseCallback 是自定义 CallBack,listener:回调监听,request.getParserType():请求类型解析  ResponseCallback(IHttpListener httpListener, Type parserType)
     //call.execute(new ResponseCallback(listener,request.getParserType()));

     //这里采用链式调用,上面的是解析
     request.getParams()
            .getPostFormBuilder()
            .url(request.getUrl())
            .id(request.getRequestId())
            .build()
            .execute(new ResponseCallback(listener, request.getParserType()));
   } else {
      throw new RuntimeException("Request param is null");
   }
}

4.2、Post Json

public void postJson(IRequest request, IHttpListener listener) {
   LogDebugUtil.e(TAG, "postForm: url=" + request.getUrl());
   if (request != null) {
     //请求参数构造,其实就是构造 Request 对象,Request 采用 build 模式,这里也一样
     //RequestParams params = request.getParams();
     //PostStringBuilder 来自 OkhttpUtils
     //PostStringBuilder postJsonBuilder = params.getPostJsonBuilder();
     //RequestCall 和 Call 对象类似,就是用来执行网络请求的
     //RequestCall call = postJsonBuilder.url(request.getUrl())
     //         .id(request.getRequestId()).build();
     //执行 call,ResponseCallback 是自定义 CallBack,listener:回调监听,request.getParserType():请求类型解析  ResponseCallback(IHttpListener httpListener, Type parserType)
     //call.execute(new ResponseCallback(listener,request.getParserType()));
            
     //这里采用链式调用,上面的是解析
      request.getParams()
            .getPostJsonBuilder()
            .url(request.getUrl())
            .id(request.getRequestId())
            .build().execute(new ResponseCallback(listener, request.getParserType()));
   } else {
      throw new RuntimeException("Request param is null");
   }
}

5、其它方法和类的封装

使用 Call.cancel() 可以立即停止掉一个正在执行的 call。如果一个线程正在写请求或者读响应,将会引发 IOException。当 call 没有必要的时候,使用这个 api 可以节约网络资源。例如当用户离开一个应用时。不管同步还是异步的 call 都可以取消。
你可以通过 tags 来同时取消多个请求。当你构建一请求时,使用 RequestBuilder.tag(tag) 来分配一个标签。之后你就可以用 OkHttpClient.cancel(tag) 来取消所有带有这个 tag 的 call。
cancelRequest 根据 tag 取消 call,StringCallback 为 String 类 Callback。

//取消 call
public void cancelRequest(String tag) {
   if (tag != null) {
      OkHttpUtils.getInstance().cancelTag(tag);
   }
}

//StringCallback 封装,解析 String 类型数据
abstract class StringCallback extends Callback<String> {
        @Override
        public String parseNetworkResponse(okhttp3.Response response, int id) throws IOException {
            return response.body().string();
        }
    }

6、总结

OkHttp 封装步骤整理
1、OkHttpClient 实例化,所有请求都是在此基础上进行的。
2、封装 Request,包括参数的传递和 url 的构造
3、封装 Callback,将数据源作为范型传递进去,根据具体需求进行解析,重写抽象方法,onSuccess()、onFailure()、onStart() 等。

140套Android优秀开源项目源码,领取地址:http://mp.weixin.qq.com/s/afPGHqfdiApALZqHsXbw-A

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容