OkHttp封裝

来源:http://www.cnblogs.com/yishaochu/archive/2017/08/31/7459929.html
-Advertisement-
Play Games

前言 上個知識點介紹了OKHttp的基本使用,在Activity中寫了大量訪問網路的代碼,這種代碼寫起來很無聊,並且對技術沒什麼提升。在真實的企業開發中,肯定是把這些代碼封裝起來,做一個庫,給Activity調用。 封裝之前我們需要考慮以下這些問題: 封裝基本的公共方法給外部調用。get請求,Pos ...


前言

上個知識點介紹了OKHttp的基本使用,在Activity中寫了大量訪問網路的代碼,這種代碼寫起來很無聊,並且對技術沒什麼提升。在真實的企業開發中,肯定是把這些代碼封裝起來,做一個庫,給Activity調用。

封裝之前我們需要考慮以下這些問題:

  • 封裝基本的公共方法給外部調用。get請求,Post請求,PostFile
  • 官方建議OkHttpClient實例只new一次,那我們網路請求庫可以做成單例模式。
  • 如果同一時間訪問同一個api多次,我們是不是應該取消之前的請求?
  • 如果用戶連接Http代理了,就不讓訪問,防止用戶通過抓包工具看我們的介面數據。
  • 每個介面都要帶上的參數如何封裝?例如app版本號,設備號,登錄之後的用戶token,這些參數可能每次請求都要帶上。
  • 返回的json字元串轉成實體對象
  • 訪問伺服器是非同步請求,如何在主線程中調用回調介面?

代碼實現

首先需要線上引用以下三個依賴庫

compile 'com.squareup.okhttp3:okhttp:3.2.0' //okhttp
compile 'com.google.code.gson:gson:2.7' //解析jsons數據
compile 'io.github.lizhangqu:coreprogress:1.0.2' //上傳下載回調監聽

新建一個HttpConfig類,用來配置一些請求參數,存儲請求伺服器的公共參數。設置連接時間等。。。

public class HttpConfig {
    private boolean debug=false;//true:debug模式
    private String userAgent="";//用戶代理 它是一個特殊字元串頭,使得伺服器能夠識別客戶使用的操作系統及版本、CPU 類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。
    private boolean agent=true;//有代理的情況能不能訪問,true:有代理能訪問 false:有代理不能訪問
    private String tagName="Http";

    private int connectTimeout=10;//連接超時時間 單位:秒
    private int writeTimeout=10;//寫入超時時間 單位:秒
    private int readTimeout=30;//讀取超時時間 單位:秒

    //通用欄位
    private List<NameValuePair> commonField=new ArrayList<>();

    public boolean isDebug() {
        return debug;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    public boolean isAgent() {
        return agent;
    }

    public void setAgent(boolean agent) {
        this.agent = agent;
    }

    public String getTagName() {
        return tagName;
    }

    public void setTagName(String tagName) {
        this.tagName = tagName;
    }

    public List<NameValuePair> getCommonField() {
        return commonField;
    }

    public int getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public int getWriteTimeout() {
        return writeTimeout;
    }

    public void setWriteTimeout(int writeTimeout) {
        this.writeTimeout = writeTimeout;
    }

    public int getReadTimeout() {
        return readTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    /**
     * 更新參數
     * @param key
     * @param value
     */
    public void updateCommonField(String key,String value){
        boolean result = true;
        for(int i=0;i<commonField.size();i++){
            NameValuePair nameValuePair = commonField.get(i);
            if(nameValuePair.getName().equals(key)){
                commonField.set(i,new NameValuePair(key,value));
                result = false;
                break;
            }
        }
        if(result){
            commonField.add(new NameValuePair(key,value));
        }
    }

    /**
     * 刪除公共參數
     * @param key
     */
    public void removeCommonField(String key){
        for(int i=commonField.size()-1;i>=0;i--){
            if(commonField.get(i).equals("key")){
                commonField.remove(i);
            }
        }
    }

    /**
     * 添加請求參數
     * @param key
     * @param value
     */
    public void addCommonField(String key,String value){
        commonField.add(new NameValuePair(key,value));
    }
}

我們給伺服器提交表單數據都是通過name跟vulue的形式提交數據,封裝了NameValuePair類。如果是上傳文件,isFile設置true就行。

public class NameValuePair {
    private String name;//請求名稱
    private String value;//請求值
    private boolean isFile=false;//是否是文件

    public NameValuePair(String name, String value){
        this.name=name;
        this.value = value;
    }

    public NameValuePair(String name, String value,boolean isFile){
        this.name=name;
        this.value = value;
        this.isFile=isFile;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public boolean isFile() {
        return isFile;
    }

    public void setFile(boolean file) {
        isFile = file;
    }
}

請求伺服器成功或者失敗都會回調Callback介面,我們封裝HttpResponseHandler抽象類來實現這個介面,對伺服器返回的數據以及狀態碼進行了簡單的過濾,最終最調用自己的onFailure跟onSuccess方法。

public abstract class HttpResponseHandler implements Callback {
    public HttpResponseHandler(){
    }

    public void onFailure(Call call, IOException e){
        onFailure(-1,e.getMessage().getBytes());
    }

    public void onResponse(Call call, Response response) throws IOException {
        int code =response.code();
        byte[] body = response.body().bytes();
        if(code>299){
            onFailure(response.code(),body);
        }else{
            Headers headers = response.headers();
            Header[] hs = new Header[headers.size()];

            for (int i=0;i<headers.size();i++){
                hs[i] = new Header(headers.name(i),headers.value(i));
            }
            onSuccess(code,hs,body);
        }
    }

    public void onFailure(int status,byte[] data){
    }

//  public void onProgress(int bytesWritten, int totalSize) {
//  }

    public abstract void onSuccess(int statusCode, Header[] headers, byte[] responseBody);
}

Get請求封裝

封裝後的HTTPCaller代碼如下,現在裡面只有get請求,如果一下子把post請求跟postFile方法貼進來,代碼有點多。

public class HTTPCaller {
    private static HTTPCaller _instance = null;
    private OkHttpClient client;//okhttp對象
    private Map<String,Call> requestHandleMap = null;//以URL為KEY存儲的請求
    private CacheControl cacheControl = null;//緩存控制器

    private Gson gson = null;

    private HttpConfig httpConfig=new HttpConfig();//配置信息

    private HTTPCaller() {}

    public static HTTPCaller getInstance(){
        if (_instance == null) {
            _instance = new HTTPCaller();
        }
        return _instance;
    }

    /**
     * 設置配置信息 這個方法必需要調用一次
     * @param httpConfig
     */
    public void setHttpConfig(HttpConfig httpConfig) {
        this.httpConfig = httpConfig;

        client = new OkHttpClient.Builder()
                .connectTimeout(httpConfig.getConnectTimeout(), TimeUnit.SECONDS)
                .writeTimeout(httpConfig.getWriteTimeout(), TimeUnit.SECONDS)
                .readTimeout(httpConfig.getReadTimeout(), TimeUnit.SECONDS)
                .build();

        gson = new Gson();
        requestHandleMap = Collections.synchronizedMap(new WeakHashMap<String, Call>());
        cacheControl =new CacheControl.Builder().noStore().noCache().build();//不使用緩存
    }

    public <T> void get(Class<T> clazz,final String url,Header[] header,final RequestDataCallback<T> callback) {
        this.get(clazz,url,header,callback,true);
    }

    /**
     * get請求
     * @param clazz json對應類的類型
     * @param url 請求url
     * @param header 請求頭
     * @param callback 回調介面
     * @param autoCancel 是否自動取消 true:同一時間請求一個介面多次  只保留最後一個
     * @param <T>
     */
    public <T> void get(final Class<T> clazz,final String url,Header[] header,final RequestDataCallback<T> callback, boolean autoCancel){
        if (checkAgent()) {
            return;
        }
        add(url,getBuilder(url, header, new MyHttpResponseHandler(clazz,url,callback)),autoCancel);
    }

    private Call getBuilder(String url, Header[] header, HttpResponseHandler responseCallback) {
        url=Util.getMosaicParameter(url,httpConfig.getCommonField());//拼接公共參數
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        builder.get();
        return execute(builder, header, responseCallback);
    }

    private Call execute(Request.Builder builder, Header[] header, Callback responseCallback) {
        boolean hasUa = false;
        if (header == null) {
            builder.header("Connection","close");
            builder.header("Accept", "*/*");
        } else {
            for (Header h : header) {
                builder.header(h.getName(), h.getValue());
                if (!hasUa && h.getName().equals("User-Agent")) {
                    hasUa = true;
                }
            }
        }
        if (!hasUa&&!TextUtils.isEmpty(httpConfig.getUserAgent())){
            builder.header("User-Agent",httpConfig.getUserAgent());
        }
        Request request = builder.cacheControl(cacheControl).build();
        Call call = client.newCall(request);
        call.enqueue(responseCallback);
        return call;
    }

    public class MyHttpResponseHandler<T> extends HttpResponseHandler {
        private Class<T> clazz;
        private String url;
        private RequestDataCallback<T> callback;

        public MyHttpResponseHandler(Class<T> clazz,String url,RequestDataCallback<T> callback){
            this.clazz=clazz;
            this.url=url;
            this.callback=callback;
        }

        @Override
        public void onFailure(int status, byte[] data) {
            clear(url);
            try {
                printLog(url + " " + status + " " + new String(data, "utf-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            sendCallback(callback);
        }

        @Override
        public void onSuccess(int status,final Header[] headers, byte[] responseBody) {
            try {
                clear(url);
                String str = new String(responseBody,"utf-8");
                printLog(url + " " + status + " " + str);
                T t = gson.fromJson(str, clazz);
                sendCallback(status,t,responseBody,callback);
            } catch (Exception e){
                if (httpConfig.isDebug()) {
                    e.printStackTrace();
                    printLog("自動解析錯誤:" + e.toString());
                }
                sendCallback(callback);
            }
        }
    }

    private void autoCancel(String function){
        Call call = requestHandleMap.remove(function);
        if (call != null) {
            call.cancel();
        }
    }

    private void add(String url,Call call) {
        add(url,call,true);
    }

    /**
     * 保存請求信息
     * @param url 請求url
     * @param call http請求call
     * @param autoCancel 自動取消
     */
    private void add(String url,Call call,boolean autoCancel) {
        if (!TextUtils.isEmpty(url)){
            if (url.contains("?")) {//get請求需要去掉後面的參數
                url=url.substring(0,url.indexOf("?"));
            }
            if(autoCancel){
                autoCancel(url);//如果同一時間對api進行多次請求,自動取消之前的
            }
            requestHandleMap.put(url,call);
        }
    }

    private void clear(String url){
        if (url.contains("?")) {//get請求需要去掉後面的參數
            url=url.substring(0,url.indexOf("?"));
        }
        requestHandleMap.remove(url);
    }

    private void printLog(String content){
        if(httpConfig.isDebug()){
            Log.i(httpConfig.getTagName(),content);
        }
    }

    /**
     * 檢查代理
     * @return
     */
    private boolean checkAgent() {
        if (httpConfig.isAgent()){
            return false;
        } else {
            String proHost = android.net.Proxy.getDefaultHost();
            int proPort = android.net.Proxy.getDefaultPort();
            if (proHost==null || proPort<0){
                return false;
            }else {
                Log.i(httpConfig.getTagName(),"有代理,不能訪問");
                return true;
            }
        }
    }

    //更新欄位值
    public void updateCommonField(String key,String value){
        httpConfig.updateCommonField(key,value);
    }

    public void removeCommonField(String key){
        httpConfig.removeCommonField(key);
    }

    public void addCommonField(String key,String value){
        httpConfig.addCommonField(key,value);
    }

    private <T> void sendCallback(RequestDataCallback<T> callback){
        sendCallback(-1,null,null,callback);
    }

    private <T> void sendCallback(int status,T data,byte[] body,RequestDataCallback<T> callback){
        CallbackMessage<T> msgData = new CallbackMessage<T>();
        msgData.body = body;
        msgData.status = status;
        msgData.data = data;
        msgData.callback = callback;

        Message msg = handler.obtainMessage();
        msg.obj = msgData;
        handler.sendMessage(msg);
    }

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            CallbackMessage data = (CallbackMessage)msg.obj;
            data.callback();
        }
    };

    private class CallbackMessage<T>{
        public RequestDataCallback<T> callback;
        public T data;
        public byte[] body;
        public int status;

        public void callback(){
            if(callback!=null){
                if(data==null){
                    callback.dataCallback(null);
                }else{
                    callback.dataCallback(status,data,body);
                }
            }
        }
    }
}
  • getInstance 用來獲取對象類的對象,因為是單例模式,這個方法無論調用多少次只會創建一個對象。
  • setHttpConfig 創建了HTTPCaller對象的時候必須要調用該方法來初始化一些對象,通過參數httpConfig獲取連接時間來初始化OkHttpClient對象,用WeakHashMap來存儲每一次的請求。還需要初始化Gson對象。
  • get 有兩個同名方法,這是方法重載,如果你想同一時間訪問同一介面多次,用下麵那個方法,最後一個一個參數傳false,如果沒有要求就用上面那個。我們看到在get方法裡面首先調用了getBuilder發起get請求,getBuilder會返回一個Call對象,這個方法的第三個參數傳入一個MyHttpResponseHandler對象,這個對象實現了Callback介面,請求伺服器成功或者失敗會把結果回調這個類。最外層調用了add方法,請求url作為key,Call作為vuelue保存到map裡面。
  • getBuilder 這個方法的第一行調用Util的getMosaicParameter方法拼接公共參數,然後根據url生成Request.Builder對象,繼續調用execute。
  • execute 首先設置請求頭信息,一般沒有特殊要求請求都不需要設置,通過builder對象的cacheControl方法設置緩存控制,通過build方法生成Request,通過OkHttpClient對象的newCall方法生成一個call對象,最後調用enqueue方法,進行非同步請求,傳入一個回調介面。

MyHttpResponseHandler類繼承我們自己寫的HttpResponseHandler抽象類,重寫兩個方法onFailure跟onSuccess。

onFailure 先從map中刪除這個請求,列印錯誤log,發送回調。
onSuccess 先從map中刪除這個請求,把json字元串轉成對象。發送回調。
  • autoCancel 從map中刪除這個請求,調用Call.call取消http請求。
  • add 有兩個同名的方法,方法重載。我們來分析後面這個。首先判斷這個url是否為空,為空的話肯定啥也幹不了。然後需要截取?前面的信息,不然參數不一樣我們就沒法判斷是不是同一時間多次請求同一介面了。然後判斷要不要自動取消。最後存入map。經過前面的分析我們都知道發起get請求的時候會調用add方法把call請求保存到map裡面去,請求成功回調中會把call從map中刪除。假如手機網路狀態不穩定,同一時間請求了10次同一個介面,都沒有回調回來,這也是為什麼我們需要在添加的時候會自動取消之前的請求。
  • clear 從map中刪除請求
  • printLog 列印log
  • sendCallback 有兩個方法,方法重載。把返回內容,狀態,byte數組,回調封裝到CallbackMessage對象,通過handerl把這個對象發送到主線程中,然後調用CallbackMessage對象的callback方法。

Post請求封裝

通過以上對HTTPCaller類各個方法的解釋,相信你知道了get請求伺服器的整個調用流程。現在我們對HTTPCaller類增加post請求的方法以及上傳文件的方法。postFile方法增加了ProgressUIListener參數,通過這個介面監聽上傳文件進度的回調。

public <T> void post(final Class<T> clazz, final String url, Header[] header, List<NameValuePair> params, final RequestDataCallback<T> callback) {
        this.post(clazz,url, header, params, callback,true);
}

/**
 *
 * @param clazz json對應類的類型
 * @param url  請求url
 * @param header 請求頭
 * @param params 參數
 * @param callback 回調
 * @param autoCancel 是否自動取消 true:同一時間請求一個介面多次  只保留最後一個
 * @param <T>
 */
public <T> void post(final Class<T> clazz,final String url, Header[] header, final List<NameValuePair> params, final RequestDataCallback<T> callback, boolean autoCancel) {
    if (checkAgent()) {
        return;
    }
    add(url,postBuilder(url, header, params, new HTTPCaller.MyHttpResponseHandler(clazz,url,callback)),autoCancel);
}

private Call postBuilder(String url, Header[] header, List<NameValuePair> form, HttpResponseHandler responseCallback) {
    try {
        if (form == null) {
            form = new ArrayList<>(2);
        }
        form.addAll(httpConfig.getCommonField());//添加公共欄位
        FormBody.Builder formBuilder = new FormBody.Builder();
        for (NameValuePair item : form) {
            formBuilder.add(item.getName(), item.getValue());
        }
        RequestBody requestBody = formBuilder.build();
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        builder.post(requestBody);
        return execute(builder, header, responseCallback);
    } catch (Exception e) {
        if (responseCallback != null)
            responseCallback.onFailure(-1, e.getMessage().getBytes());
    }
    return null;
}

/**
 * 上傳文件
 * @param clazz json對應類的類型
 * @param url 請求url
 * @param header 請求頭
 * @param form 請求參數
 * @param callback 回調
 * @param <T>
 */
public <T> void postFile(final Class<T> clazz, final String url, Header[] header,List<NameValuePair> form,final RequestDataCallback<T> callback) {
    postFile(url, header, form, new HTTPCaller.MyHttpResponseHandler(clazz,url,callback),null);
}

/**
 * 上傳文件
 * @param clazz json對應類的類型
 * @param url 請求url
 * @param header 請求頭
 * @param form 請求參數
 * @param callback 回調
 * @param progressUIListener  上傳文件進度
 * @param <T>
 */
public <T> void postFile(final Class<T> clazz, final String url, Header[] header,List<NameValuePair> form,final RequestDataCallback<T> callback,ProgressUIListener progressUIListener) {
    add(url, postFile(url, header, form, new HTTPCaller.MyHttpResponseHandler(clazz,url,callback),progressUIListener));
}

/**
 * 上傳文件
 * @param clazz json對應類的類型
 * @param url 請求url
 * @param header 請求頭
 * @param name 名字
 * @param fileName 文件名
 * @param fileContent 文件內容
 * @param callback 回調
 * @param <T>
 */
public <T> void postFile(final Class<T> clazz,final String url,Header[] header,String name,String fileName,byte[] fileContent,final RequestDataCallback<T> callback){
    postFile(clazz,url,header,name,fileName,fileContent,callback,null);
}


/**
 * 上傳文件
 * @param clazz json對應類的類型
 * @param url 請求url
 * @param header 請求頭
 * @param name 名字
 * @param fileName 文件名
 * @param fileContent 文件內容
 * @param callback 回調
 * @param progressUIListener 回調上傳進度
 * @param <T>
 */
public <T> void postFile(Class<T> clazz,final String url,Header[] header,String name,String fileName,byte[] fileContent,final RequestDataCallback<T> callback,ProgressUIListener progressUIListener) {
    add(url,postFile(url, header,name,fileName,fileContent,new HTTPCaller.MyHttpResponseHandler(clazz,url,callback),progressUIListener));
}

private Call postFile(String url, Header[] header,List<NameValuePair> form,HttpResponseHandler responseCallback,ProgressUIListener progressUIListener){
    try {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        MediaType mediaType = MediaType.parse("application/octet-stream");

        form.addAll(httpConfig.getCommonField());//添加公共欄位

        for(int i=form.size()-1;i>=0;i--){
            NameValuePair item = form.get(i);
            if(item.isFile()){//上傳文件
                File myFile = new File(item.getValue());
                if (myFile.exists()){
                    String fileName = Util.getFileName(item.getValue());
                    builder.addFormDataPart(item.getName(), fileName,RequestBody.create(mediaType, myFile));
                }
            }else{
                builder.addFormDataPart(item.getName(), item.getValue());
            }
        }

        RequestBody requestBody;
        if(progressUIListener==null){//不需要回調進度
            requestBody=builder.build();
        }else{//需要回調進度
            requestBody = ProgressHelper.withProgress(builder.build(),progressUIListener);
        }
        Request.Builder requestBuider = new Request.Builder();
        requestBuider.url(url);
        requestBuider.post(requestBody);
        return execute(requestBuider, header, responseCallback);
    } catch (Exception e) {
        e.printStackTrace();
        Log.e(httpConfig.getTagName(),e.toString());
        if (responseCallback != null)
            responseCallback.onFailure(-1, e.getMessage().getBytes());
    }
    return null;
}

private Call postFile(String url, Header[] header,String name,String filename,byte[] fileContent, HttpResponseHandler responseCallback,ProgressUIListener progressUIListener) {
    try {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        MediaType mediaType = MediaType.parse("application/octet-stream");
        builder.addFormDataPart(name,filename,RequestBody.create(mediaType, fileContent));

        List<NameValuePair> form = new ArrayList<>(2);
        form.addAll(httpConfig.getCommonField());//添加公共欄位
        for (NameValuePair item : form) {
            builder.addFormDataPart(item.getName(),item.getValue());
        }

        RequestBody requestBody;
        if(progressUIListener==null){//不需要回調進度
            requestBody=builder.build();
        }else{//需要回調進度
            requestBody = ProgressHelper.withProgress(builder.build(),progressUIListener);
        }
        Request.Builder requestBuider = new Request.Builder();
        requestBuider.url(url);
        requestBuider.post(requestBody);
        return execute(requestBuider, header,responseCallback);
    } catch (Exception e) {
        if (httpConfig.isDebug()) {
            e.printStackTrace();
            Log.e(httpConfig.getTagName(), e.toString());
        }
        if (responseCallback != null)
            responseCallback.onFailure(-1, e.getMessage().getBytes());
    }
    return null;
}

增加下載文件的方法

get請求跟post請求都有了,上傳文件就是post請求,繼續增加downloadFile方法用來下載文件,通用的MyHttpResponseHandler不能處理回調,又增加了DownloadFileResponseHandler類處理下載回調。

public void downloadFile(String url, String saveFilePath, Header[] header, ProgressUIListener progressUIListener) {
        downloadFile(url,saveFilePath, header, progressUIListener,true);
    }

public void downloadFile(String url,String saveFilePath, Header[] header,ProgressUIListener progressUIListener,boolean autoCancel) {
    if (checkAgent()) {
        return;
    }
    add(url,downloadFileSendRequest(url,saveFilePath, header, progressUIListener),autoCancel);
}

private Call downloadFileSendRequest(String url, final String saveFilePath, Header[] header, final ProgressUIListener progressUIListener){
    Request.Builder builder = new Request.Builder();
    builder.url(url);
    builder.get();
    return execute(builder, header, new HTTPCaller.DownloadFileResponseHandler(url,saveFilePath,progressUIListener));
}

public class DownloadFileResponseHandler implements Callback {
    private String saveFilePath;
    private ProgressUIListener progressUIListener;
    private String url;

    public DownloadFileResponseHandler(String url,String saveFilePath,ProgressUIListener progressUIListener){
        this.url=url;
        this.saveFilePath=saveFilePath;
        this.progressUIListener=progressUIListener;
    }

    @Override
    public void onFailure(Call call, IOException e) {
        clear(url);
        try {
            printLog(url + " " + -1 + " " + new String(e.getMessage().getBytes(), "utf-8"));
        } catch (UnsupportedEncodingException encodingException) {
            encodingException.printStackTrace();
        }
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        printLog(url + " code:" + response.code());
        clear(url);

        ResponseBody responseBody = ProgressHelper.withProgress(response.body(),progressUIListener);
        BufferedSource source = responseBody.source();

        File outFile = new File(saveFilePath);
        outFile.delete();
        outFile.createNewFile();

        BufferedSink sink = Okio.buffer(Okio.sink(outFile));
        source.readAll(sink);
        sink.flush();
        source.close();
    }
}

RequestDataCallback介面,封裝了三個方法,自己想要什麼數據就去重寫對應的方法。

public abstract class RequestDataCallback<T> {
    //返回json對象
    public void dataCallback(T obj) {
    }

    //返回http狀態和json對象
    public void dataCallback(int status, T obj) {
        dataCallback(obj);
    }

    //返回http狀態、json對象和http原始數據
    public void dataCallback(int status, T obj, byte[] body) {
        dataCallback(status, obj);
    }
}

還有Util公共類,有三個靜態方法。

public class Util {
    /**
     * 獲取文件名稱
     * @param filename
     * @return
     */
    public static String getFileName(String filename){
        int start=filename.lastIndexOf("/");
//        int end=filename.lastIndexOf(".");
        if(start!=-1){
            return filename.substring(start+1,filename.length());
        }else{
            return null;
        }
    }

    /**
     * 拼接公共參數
     * @param url
     * @param commonField
     * @return
     */
    public static String getMosaicParameter(String url, List<NameValuePair> commonField){
        if (TextUtils.isEmpty(url))
            return "";
        if (url.contains("?")) {
            url = url + "&";
        } else {
            url = url + "?";
        }
        url += getCommonFieldString(commonField);
        return url;
    }

    private static String getCommonFieldString(List<NameValuePair> commonField){
        StringBuffer sb = new StringBuffer();
        try{
            int i=0;
            for (NameValuePair item:commonField) {
                if(i>0){
                    sb.append("&");
                }
                sb.append(item.getName());
                sb.append('=');
                sb.append(URLEncoder.encode(item.getValue(),"utf-8"));
                i++;
            }
        }catch (Exception e){

        }
        return  sb.toString();
    }
}

源碼下載

OkHttp封裝之後如何使用


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 蘋果電腦獲取Android Studio的發佈版SHA1和開發版SHA1 ...
  • 一、 安裝插件 cordova plugin add ionic-plugin-keyboard 二、 軟鍵盤顯示監聽 window.addEventListener('native.keyboardshow', function (e) { // todo 進行鍵盤可用時操作 //e.keyboa ...
  • 一、當Activity啟動後EditText直接獲取了焦點,此時軟鍵盤會自動彈出,這種體驗並不是很好,因此要做的Activity啟動不自動彈出軟鍵盤,只需要在Manifest中對應的Activity添加以下這句話就可以了: ...
  • Android簡單的編寫一個txt閱讀器(沒有處理字元編碼),適用於新手學習   本程式只是使用了一些基本的知識點編寫了一個比較簡單粗陋的txt文本閱讀器,效率不高,只適合新手練習。所以大神勿噴。   其實想到編寫這種程式源自本人之前喜歡看小說,而很多小說更新太慢,所以本人就只能找一個完本的.tx... ...
  • 之前用AVFoundation自定義相機做了拍照與視頻相關的東西,為什麼要自定義呢?主要是提供更個性化的交互設計,符合app主題,對於視頻來說,也便於提供更多豐富有趣的功能。前段時間整理了下拍照部分的功能,主要分為以下五個部分 1.初始化,建立會話,獲取攝像頭 使用AVCaptureSessionP ...
  • 最後 各位小伙伴們 又不懂或不清楚的可以給我留言 歡迎大家給我提出建議 或是指出問題 我們彼此都需要一個學習的過程 ...
  • 添加了 1 2 3 ( 1, 2, 3 ) 最前面插入了4 ( 4, 1, 2, 3 ) 刪除了1 ( 4, 2, 3 ) 刪除了0 (一個不存在的元素) ( 4, 2, 3 ) ...
  • 在iOS中常用的框架是Quartz 2D,它是Core Graphics框架的一部分,是一個強大的二維圖像繪製引擎。我們日常開發所用到的UIKit的組件都是由Core Graphics框架進行繪製的。當我們導入UIKit框架時,會自動導入Core Graphics框架。 在iOS中繪圖一般分為以下幾 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...