【從 0 開始開發(fā)一款直播 APP】4.3 網(wǎng)絡(luò)封裝之 OkHttp -- 封裝 GET尚胞,POST FORM硬霍,POST JSON

本文為菜鳥窩作者蔣志碧的連載×眩“從 0 開始開發(fā)一款直播 APP ”系列來聊聊時下最火的直播 APP唯卖,如何完整的實現(xiàn)一個類"騰訊直播"的商業(yè)化項目
視頻地址:http://www.cniao5.com/course/10121


【從 0 開始開發(fā)一款直播 APP】4.1 網(wǎng)絡(luò)封裝之 Okhttp -- 基礎(chǔ)回顧
【從 0 開始開發(fā)一款直播 APP】4.2 網(wǎng)絡(luò)封裝之 OkHttp -- GET粱玲,POST,前后端交互
【從 0 開始開發(fā)一款直播 APP】4.3 網(wǎng)絡(luò)封裝之 OkHttp -- 封裝 GET拜轨,POST FORM抽减,POST JSON
【從 0 開始開發(fā)一款直播 APP】4.4 網(wǎng)絡(luò)封裝之 OkHttp -- 網(wǎng)絡(luò)請求實現(xiàn)直播登錄


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

封裝之前橄碾,再次熟悉一下 OkHttp 請求網(wǎng)絡(luò)的大致步驟卵沉,讓封裝的時候思路清晰一點(diǎn)。

//1法牲、初始化 OkHttpClient 對象
private OkHttpClient mHttpClient = new OkHttpClient();
//2史汗、構(gòu)造Request
//2.1 構(gòu)造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拒垃、執(zhí)行 call
call.enqueue(new Callback() {
     //請求失敗調(diào)用
     @Override
     public void onFailure(Request request, IOException e) {
      }
     //請求成功調(diào)用
      @Override
      public void onResponse(Response response) throws IOException {
      }
});

OkHttp 請求基本步驟就是這幾步停撞,看起來很簡單,要封裝自然也要考慮到這里面的方方面面恶复,根據(jù)自己的業(yè)務(wù)需求進(jìn)行相應(yīng)的封裝怜森。封裝是基于鴻洋大神的 OkHttpUtils,站在巨人的肩膀上谤牡,我們封裝起來會更加簡單副硅。

1、OkHttpClient 實例化

聲明一個私有的 OkHttpClient 對象翅萤,對其進(jìn)行構(gòu)建并設(shè)置超時信息恐疲,而 AsyncHttp 類則是我們要進(jìn)行網(wǎng)絡(luò)訪問對外公開的類,OkHttpClient 官方建議我們設(shè)置成單例模式套么。

public class AsyncHttp {
    private static AsyncHttp mInstance;
    //初始化操作培己,設(shè)置超時時間
    private OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectTimeout(20 * 1000, TimeUnit.MILLISECONDS)
            .readTimeout(20 * 1000, TimeUnit.MILLISECONDS)
            .build();
    //私有構(gòu)造函數(shù),初始化 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胚泌、構(gòu)造 GET 請求

get 請求官方給我們的是沒有參數(shù)的省咨,不滿足我們需求,根據(jù)我們的需求將其進(jìn)行封裝玷室。GET 請求需要 Request 對象傳參零蓉,還需要回調(diào) CallBack,因此需要添加 Request 基類和 CallBack 基類穷缤。

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

IRequest 是 Request 的封裝類敌蜂,IHttpListener 是回調(diào) CallBack 的監(jiān)聽。

2.1津肛、封裝 IRequest

我們知道官方提供的 Request 類是對請求信息進(jìn)行封裝章喉,包括請求地址 url,請求方法 method,請求頭 head秸脱,請求體 RequestBody落包,取消 http 請求的標(biāo)志 tag。IRequest 是一個抽象類撞反,請求信息封裝在 RequestParams 類里面妥色,IRequest 對外提供了一些方法,獲取請求參數(shù)遏片,獲取/設(shè)置唯一標(biāo)識,獲取 Url撮竿,獲取解析的數(shù)據(jù)類型吮便。

//IDontObfuscate 實現(xiàn) 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();
    //請求網(wǎng)絡(luò)直播登錄的唯一標(biāo)識
    public int mRequestId = 0;
    protected int mDraw = 0;
  
    public IRequest() {
    }

    /**
     * 接口請求參數(shù)
     */
    public RequestParams getParams() {
        return mParams;
    }

    /**
     * 設(shè)置接口請求唯一標(biāo)識
     */
    public void setRequestId(int requestId) {
        mRequestId = requestId;
    }

    /**
     * 返回請求接口唯一標(biāo)識
     */
    public int getRequestId() {
        return mRequestId;
    }

    /**
     * 當(dāng)前接口的url地址
     */
    public abstract String getUrl();

    /**
     * 獲取解析類型
     */
    public abstract Type getParserType();

    /**
     * 返回服務(wù)器接口地址
     */
    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 請求參數(shù)封裝
參數(shù)封裝類髓需,顧名思義就是對參數(shù)的保存和移除,該類適用于 GET 和 POST 參數(shù)房蝉,包括 url 參數(shù)封裝僚匆,stream 參數(shù)封裝,file 參數(shù)封裝搭幻,file 數(shù)組參數(shù)封裝咧擂,url 對象參數(shù)封裝。全部都是存儲在 ConcurrentHashMap 對象中檀蹋,通過鍵值對進(jìn)行存儲松申,put 方法有很多重載,對不同形式的參數(shù)采取不同的添加方式俯逾。

public class RequestParams implements Serializable{
    
    //二進(jìn)制流數(shù)據(jù)
    public final static String APPLICATION_OCTET_STREAM = "application/octet-stream";
    //JSON數(shù)據(jù)格式    
    public final static String APPLICATION_JSON = "application/json";
    protected final static String LOG_TAG = "RequestParams";
    
    //存儲 url 參數(shù)
    protected final ConcurrentHashMap<String, String> urlParams = new ConcurrentHashMap<String, String>();
    //存儲 stream 參數(shù)
    protected final ConcurrentHashMap<String, StreamWrapper> streamParams = new ConcurrentHashMap<String, StreamWrapper>();
    //存儲 file 參數(shù)
    protected final ConcurrentHashMap<String, FileWrapper> fileParams = new ConcurrentHashMap<String, FileWrapper>();
    //存儲 fileArray 參數(shù)
    protected final ConcurrentHashMap<String, List<FileWrapper>> fileArrayParams = new ConcurrentHashMap<String, List<FileWrapper>>();
    //存儲 url 對象參數(shù)
    protected final ConcurrentHashMap<String, Object> urlParamsWithObjects = new ConcurrentHashMap<String, Object>();

    //是否重復(fù)
    protected boolean isRepeatable;
    //標(biāo)志 multipart/form-data:需要在表單中進(jìn)行文件上傳時贸桶,就需要使用該格式
    protected boolean forceMultipartEntity = false;
    //用戶 json 數(shù)據(jù)流
    protected boolean useJsonStreamer;
    //自動關(guān)閉輸入流標(biāo)志
    protected boolean autoCloseInputStreams;
    //保存上傳有效載荷所需的時間
    protected String elapsedFieldInJsonStreamer = "_elapsed";
    //指定編碼格式
    protected String contentEncoding = "utf-8";
    private Gson mGson = new Gson();
  
    /**
     * 構(gòu)造一個空的 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 "";
    }

    /**
     * 構(gòu)造一個新的RequestParams實例,該實例包含指定map中的鍵/值字符串參數(shù)桌肴。
     */
    public RequestParams(Map<String, String> source) {
        if (source != null) {
            for (Map.Entry<String, String> entry : source.entrySet()) {
                put(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 構(gòu)造一個新的RequestParams實例皇筛,并使用單個初始鍵/值填充
     */
    public RequestParams(final String key, final String value) {
        this(new HashMap<String, String>() {{
            put(key, value);
        }});
    }

    /**
     * 構(gòu)造一個新的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");
        }
    }

    /**
     * 如果設(shè)置為true坠七,即使沒有文件或流被發(fā)送水醋,也會將Content-Type頭部強(qiáng)制為“multipart/form-data”
     * multipart/form-data:需要在表單中進(jìn)行文件上傳時,就需要使用該格式
     */
    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);
        }
    }

    /**
     * 將自定義提供的文件內(nèi)容類型和文件名稱添加到 fileArrayParams
     */
    public void put(String key, File files[]) throws FileNotFoundException {
        put(key, files, null, null);
    }

    /**
     * 將自定義提供的文件內(nèi)容類型和文件名稱添加到 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);
        }
    }

    /**
     * 將自定義提供的文件內(nèi)容類型和文件名添加到 fileParams
     */
    public void put(String key, File file) throws FileNotFoundException {
        put(key, file, null, null);
    }

    /**
     * 將自定義提供的文件內(nèi)容類型和文件名添加到 fileParams
     */
    public void put(String key, String customFileName, File file) throws FileNotFoundException {
        put(key, file, null, customFileName);
    }

    /**
     * 將自定義提供的文件內(nèi)容類型和文件名添加到 fileParams
     */
    public void put(String key, File file, String contentType) throws FileNotFoundException {
        put(key, file, contentType, null);
    }

    /**
     * 將自定義提供的文件內(nèi)容類型和文件名添加到 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) {
                // 向后兼容灼捂,這將導(dǎo)致“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);
    }

    /**
     * 監(jiān)測字段是否被定義
     */
    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 對象時設(shè)置一個附加字段离例,以保存上載有效載荷所需的時間(以毫秒為單位)。 默認(rèn)情況下悉稠,此字段設(shè)置為“_elapsed”宫蛆。
     * 要禁用此功能,請將此方法調(diào)用為null作為字段值。
     */
    public void setElapsedFieldInJsonStreamer(String value) {
        this.elapsedFieldInJsonStreamer = value;
    }

    /**
     * 設(shè)置全局標(biāo)志耀盗,用于確定在成功上傳時是否自動關(guān)閉輸入流想虎。
     */
    public void setAutoCloseInputStreams(boolean flag) {
        autoCloseInputStreams = flag;
    }

    /**
     * http get builder 參數(shù)封裝構(gòu)造類
     */
    public GetBuilder getGetBuilder() {
        GetBuilder getBuilder = new GetBuilder();
        //添加參數(shù) 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 請求添加參數(shù) url后面添加
        for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
            Log.i("log", "getBuilder value:" + entry.getValue());
            postFormBuilder.addParams(entry.getKey(), entry.getValue());
        }
        //post 請求添加參數(shù)  file 文件參數(shù)
        for (ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
            postFormBuilder.addFile(entry.getKey(), entry.getValue().file.getAbsolutePath(), entry.getValue().file);
        }
        //文件對象列表存儲相關(guān)信息
        //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();
        //將對象轉(zhuǎn)成 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;//是否自動關(guān)閉

        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:二進(jìn)制流數(shù)據(jù)標(biāo)志
        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() 方法舌厨,我們按照類似需求進(jìn)行添加相應(yīng)的方法。
onStart() 方法用于網(wǎng)絡(luò)請求之前的一些操作忿薇,onSuccess() 網(wǎng)絡(luò)請求成功的回調(diào)裙椭,onFailure() 網(wǎng)絡(luò)請求成功的回調(diào)。

public interface IHttpListener {
   //請求網(wǎng)絡(luò)之前回調(diào)(對話框提示信息等)  requestId:網(wǎng)絡(luò)請求唯一標(biāo)識
   void onStart(int requestId);
   //請求網(wǎng)絡(luò)成功回調(diào)  requestId:網(wǎng)絡(luò)請求唯一標(biāo)識署浩,response:Response 對象
   void onSuccess(int requestId, Response response);
   //請求網(wǎng)絡(luò)失敗回調(diào)  requestId:網(wǎng)絡(luò)請求唯一標(biāo)識揉燃,httpStatus:狀態(tài)碼,error:錯誤信息
   void onFailure(int requestId, int httpStatus, Throwable error);
}

兩個重要的參數(shù)算是封裝完了筋栋,下面開始 GET 請求方法實現(xiàn)炊汤。

public void get(IRequest request, IHttpListener listener) {
   LogDebugUtil.e(TAG, "post: url=" + request.getUrl());
   if (request != null) {
     //請求參數(shù)構(gòu)造,其實就是構(gòu)造 Request 對象,Request 采用 build 模式弊攘,這里也一樣
     //RequestParams params = request.getParams();
     //GetBuilder 來自 OkHttpUtils
     //GetBuilder getBuilder = params.getGetBuilder();
     //RequestCall 和 Call 對象類似抢腐,就是用來執(zhí)行網(wǎng)絡(luò)請求的
     //RequestCall call = getBuilder.url(request.getUrl())
     //         .id(request.getRequestId()).build();
     //執(zhí)行 call,ResponseCallback是自定義 CallBack襟交,listener:回調(diào)監(jiān)聽迈倍,request.getParserType():請求類型解析  ResponseCallback(IHttpListener httpListener, Type parserType)
     //call.execute(new ResponseCallback(listener,request.getParserType()));
     
      //鏈?zhǔn)秸{(diào)用,上面是分析調(diào)用過程
      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 寫了示例授瘦,根據(jù)這個示例,我們來封裝一個自己的 CallBack竟宋。



先定義一個 Bean提完,針對下面這個 json 串進(jìn)行封裝,這個是登錄的請求信息丘侠。

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

創(chuàng)建一個 Response 對象徒欣,聲明需要解析的數(shù)據(jù)

//IDontObfuscate 是一個實現(xiàn)了 Serializable 接口的序列化抽象類,上面已經(jīng)講過了
public class Response<T>  extends IDontObfuscate {
   public int status;//狀態(tài)碼
   public String msg;//狀態(tài)信息
   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 回調(diào)監(jiān)聽
   private Type mParserType;//數(shù)據(jù)轉(zhuǎn)換類型
    
   public ResponseCallback(IHttpListener httpListener, Type parserType) {
      mHttpListener = httpListener;
      mParserType = parserType;
   }

  //請求網(wǎng)絡(luò)之前回調(diào)
   @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);
                  //解析數(shù)據(jù),返回解析之后的數(shù)據(jù)
                  responseData = mGson.fromJson(content,mParserType);
               } catch (JsonSyntaxException e) {
                  onError(null,e,id);
               }
            }
         } else {
            onError(null, new Exception("net error"),id);
         }
      }
      return responseData;
   }

  //請求錯誤回調(diào)
   @Override
   public void onError(Call call, Exception e, int id) {
      if (mHttpListener != null) {
         if (!call.isCanceled()) {
            mHttpListener.onFailure(id, 0, e);
         }
      }
   }

  //請求成功回調(diào)
   @Override
   public void onResponse(Response response, int id) {
      if (mHttpListener != null) {
         mHttpListener.onSuccess(id, response);
      }
   }
}

OkHttpUtils 中 Callback 源碼

public abstract class Callback<T>{
    /**
     * UI Thread 請求網(wǎng)絡(luò)之前調(diào)用
     */
    public void onBefore(Request request, int id){
    }

    /**
     * UI Thread 請求網(wǎng)絡(luò)之后調(diào)用
     */
    public void onAfter(int id){
    }

    /**
     * UI Thread 更新進(jìn)度條
     */
    public void inProgress(float progress, long total , int id){
    }

    /**
     * 如果在 parseNetworkResponse 中解析響應(yīng)代碼挪捕,則應(yīng)該使此方法返回 true粗梭。
     */
    public boolean validateReponse(Response response, int id)
    {
        return response.isSuccessful();
    }

    /**
     * Thread Pool Thread 根據(jù)示例可以看出,該方法是解析數(shù)據(jù)的级零,T 是數(shù)據(jù)源
     */
    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、構(gòu)造 POST 請求

post 請求這里封裝兩個鉴嗤,F(xiàn)orm 表單和 Json 字符串的 Post 提交封裝斩启。

4.1、Post Form

post 和 get 類似醉锅,之前 RequestParams 請求參數(shù)封裝類已經(jīng)構(gòu)造完成兔簇,這里可以直接使用。

public void postForm(IRequest request, IHttpListener listener) {
   LogDebugUtil.e(TAG, "postForm: url=" + request.getUrl());
   if (request != null) {
     //請求參數(shù)構(gòu)造,其實就是構(gòu)造 Request 對象硬耍,Request 采用 build 模式垄琐,這里也一樣
     //RequestParams params = request.getParams();
     //PostFormBuilder 來自 OkhttpUtils
     //PostFormBuilder postFormBuilder = params.getPostFormBuilder();
     //RequestCall 和 Call 對象類似,就是用來執(zhí)行網(wǎng)絡(luò)請求的
     //RequestCall call = postFormBuilder.url(request.getUrl())
     //         .id(request.getRequestId()).build();
     //執(zhí)行 call默垄,ResponseCallback 是自定義 CallBack此虑,listener:回調(diào)監(jiān)聽,request.getParserType():請求類型解析  ResponseCallback(IHttpListener httpListener, Type parserType)
     //call.execute(new ResponseCallback(listener,request.getParserType()));

     //這里采用鏈?zhǔn)秸{(diào)用口锭,上面的是解析
     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) {
     //請求參數(shù)構(gòu)造,其實就是構(gòu)造 Request 對象介杆,Request 采用 build 模式鹃操,這里也一樣
     //RequestParams params = request.getParams();
     //PostStringBuilder 來自 OkhttpUtils
     //PostStringBuilder postJsonBuilder = params.getPostJsonBuilder();
     //RequestCall 和 Call 對象類似,就是用來執(zhí)行網(wǎng)絡(luò)請求的
     //RequestCall call = postJsonBuilder.url(request.getUrl())
     //         .id(request.getRequestId()).build();
     //執(zhí)行 call春哨,ResponseCallback 是自定義 CallBack荆隘,listener:回調(diào)監(jiān)聽,request.getParserType():請求類型解析  ResponseCallback(IHttpListener httpListener, Type parserType)
     //call.execute(new ResponseCallback(listener,request.getParserType()));
            
     //這里采用鏈?zhǔn)秸{(diào)用赴背,上面的是解析
      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() 可以立即停止掉一個正在執(zhí)行的 call。如果一個線程正在寫請求或者讀響應(yīng)凰荚,將會引發(fā) IOException燃观。當(dāng) call 沒有必要的時候,使用這個 api 可以節(jié)約網(wǎng)絡(luò)資源便瑟。例如當(dāng)用戶離開一個應(yīng)用時缆毁。不管同步還是異步的 call 都可以取消。
你可以通過 tags 來同時取消多個請求到涂。當(dāng)你構(gòu)建一請求時脊框,使用 RequestBuilder.tag(tag) 來分配一個標(biāo)簽。之后你就可以用 OkHttpClient.cancel(tag) 來取消所有帶有這個 tag 的 call践啄。
cancelRequest 根據(jù) tag 取消 call浇雹,StringCallback 為 String 類 Callback。

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

//StringCallback 封裝屿讽,解析 String 類型數(shù)據(jù)
abstract class StringCallback extends Callback<String> {
        @Override
        public String parseNetworkResponse(okhttp3.Response response, int id) throws IOException {
            return response.body().string();
        }
    }

6昭灵、總結(jié)

OkHttp 封裝步驟整理
1、OkHttpClient 實例化,所有請求都是在此基礎(chǔ)上進(jìn)行的虎锚。
2硫痰、封裝 Request,包括參數(shù)的傳遞和 url 的構(gòu)造
3窜护、封裝 Callback效斑,將數(shù)據(jù)源作為范型傳遞進(jìn)去,根據(jù)具體需求進(jìn)行解析柱徙,重寫抽象方法缓屠,onSuccess()、onFailure()护侮、onStart() 等敌完。

140套Android優(yōu)秀開源項目源碼,領(lǐng)取地址:http://mp.weixin.qq.com/s/afPGHqfdiApALZqHsXbw-A

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末羊初,一起剝皮案震驚了整個濱河市滨溉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌长赞,老刑警劉巖晦攒,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異得哆,居然都是意外死亡脯颜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門贩据,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栋操,“玉大人,你說我怎么就攤上這事饱亮》剑” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵近尚,是天一觀的道長蠕啄。 經(jīng)常有香客問我,道長戈锻,這世上最難降的妖魔是什么歼跟? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮格遭,結(jié)果婚禮上哈街,老公的妹妹穿的比我還像新娘。我一直安慰自己拒迅,他們只是感情好骚秦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布她倘。 她就那樣靜靜地躺著,像睡著了一般作箍。 火紅的嫁衣襯著肌膚如雪硬梁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天胞得,我揣著相機(jī)與錄音荧止,去河邊找鬼。 笑死阶剑,一個胖子當(dāng)著我的面吹牛跃巡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牧愁,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼素邪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了猪半?” 一聲冷哼從身側(cè)響起兔朦,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎磨确,沒想到半個月后烘绽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俐填,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了翔忽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片英融。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖歇式,靈堂內(nèi)的尸體忽然破棺而出驶悟,到底是詐尸還是另有隱情,我是刑警寧澤材失,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布痕鳍,位于F島的核電站,受9級特大地震影響龙巨,放射性物質(zhì)發(fā)生泄漏笼呆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一旨别、第九天 我趴在偏房一處隱蔽的房頂上張望诗赌。 院中可真熱鬧,春花似錦秸弛、人聲如沸铭若。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叼屠。三九已至瞳腌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镜雨,已是汗流浹背嫂侍。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冷离,地道東北人吵冒。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像西剥,于是被迫代替她去往敵國和親痹栖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容