android 設計一個簡易的Http網路請求框架
一.開發初衷:最近專案中需要用到版本升級這一塊,需要用到一些基本的資料請求與檔案下載功能。之前做專案都是用別人的網路框架,類似retrofit 、 okhttp、 fresco等框架,用的多了,發現這幾個網路請求框架,無非都是
按解決以下幾個問題為導向的:
1.怎麼發請求?
2.Cookie的問題。
3.如何停止請求(好像上面提到的幾個框架沒有停止請求的概念,因為停止請求常用用SOcket長連線協議中,
而http是短連線,只要觸發了請求,就失去了控制一樣。)
4.請求的併發?
5.如何管理請求的優先順序(類似http這種協議請求,幾乎可以忽略,請求的優先順序常用於socket協議中)
一直都想寫一個自己的網路請求框架,藉此專案機會,剛好用上了,現將設計思路與原始碼貢獻出來與各位一起交流學習,如有寫的不好,請各位大神,批評指正,謝謝。
先從回撥介面說起:這個框架中主要有兩類回撥
第一類為普通的字串請求(類似json都可以視為一種字串,只是一種特殊的格式封裝的資料)
第二類為檔案類的byte流資料。
package com.example.lxb.hellohttp.httpclient.listener; /** * 回撥基類介面 * Created by lxb on 2017/4/12. */ public abstract class BaseRequestListener<T> { public abstract void onSuccess(T result); public abstract void onFailure(T result); public void onExcetion(T e) { } public void Excetion(String e){ } public void onLoading(long total,long curProgress){ } }
在這個類中將回調共有的的方法封裝出來,如果沒有特殊的回撥行為可以直接用這個基類作為回撥,否則可以自己去擴充套件。
這裡我還寫了一個檔案的監聽器:
package com.example.lxb.hellohttp.httpclient.listener; /** * 檔案監聽器 * * Created by lxb on 2017/4/14. */ public class FileListener<T> extends BaseRequestListener<T> { @Override public void onSuccess(T result) { } @Override public void onFailure(T result) { } public void Excetion(String e){ } public void onLoading(long total,long curProgress){ } }
二。因網路請求本身就是一種I/O操作,並且是一種阻塞式的請求。如果直接放在主執行緒中進行很明顯會影響主執行緒的執行,且android系統中不允許這樣幹。
鑑於此,本框架中的所有請求均在一個新的執行緒中進行。
先來看普通字串的請求執行緒:
package com.example.lxb.hellohttp.httpclient.client; import com.example.lxb.hellohttp.httpclient.HttpClient; import com.example.lxb.hellohttp.httpclient.handler.MsgHandler; import com.example.lxb.hellohttp.httpclient.listener.BaseRequestListener; import com.example.lxb.hellohttp.httpclient.request.RequestParams; /** * 請求執行緒 * * Created by lxb on 2017/4/12. */ public class RequestThread extends Thread { private HttpClient httpClient; private MsgHandler response; private RequestParams requestParams; public RequestParams getRequestParams() { return requestParams; } public void setRequestParams(RequestParams requestParams) { this.requestParams = requestParams; } public HttpClient getHttpClient() { return httpClient; } public void setHttpClient(HttpClient httpClient) { this.httpClient = httpClient; } public MsgHandler getResponse() { return response; } public void setResponse(MsgHandler response) { this.response = response; } @Override public void run() { super.run(); this.httpClient.execute(requestParams,response); } }
檔案請求執行緒:
package com.example.lxb.hellohttp.httpclient.client; import com.example.lxb.hellohttp.httpclient.HttpClient; import com.example.lxb.hellohttp.httpclient.handler.MsgHandler; import com.example.lxb.hellohttp.httpclient.request.FileRequest; /** * 檔案網路請求執行緒 * * Created by lxb on 2017/4/13. */ public class FileReuqestThread extends Thread { private HttpClient httpClient; private MsgHandler response; private FileRequest fileRequest; public FileRequest getFileRequest() { return fileRequest; } public void setFileRequest(FileRequest fileRequest) { this.fileRequest = fileRequest; } public HttpClient getHttpClient() { return httpClient; } public void setHttpClient(HttpClient httpClient) { this.httpClient = httpClient; } public MsgHandler getResponse() { return response; } public void setResponse(MsgHandler response) { this.response = response; } @Override public void run() { super.run(); this.httpClient.execute(fileRequest,response); } }
這裡完全可以使用一個請求執行緒,但為區分不同的請求型別還是寫了兩個類,
兩個類的行為完全一致:
1.保持真正幹活類例項物件的引用(後面會講,別急^_^)
2.繫結請求物件
3.訊息分發控制代碼(主要用來解決android執行緒的通訊問題,後面會講)
既然說到這時直接把請求物件都給出來吧:
普通字元 串請求物件,如果是進行這類的請求,需要在這裡進行設定引數:
package com.example.lxb.hellohttp.httpclient.request; /** * 請求基類 * Created by lxb on 2017/4/13. */ public class Request { private String URL; private String method; public String getURL() { return URL; } public void setURL(String URL) { this.URL = URL; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }
不好意思,上面是一個請求基類,
下面才給出真正的普通字串請求物件:
package com.example.lxb.hellohttp.httpclient.request; import android.text.TextUtils; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; /** * Created by lxb on 2017/4/11. */ public class RequestParams extends Request{ private String defaultMethod = "POST"; private Map<String,String> params = new HashMap<>(); public Map<String, String> getParams() { return params; } public void setParams(Map<String, String> params) { this.params = params; } }
檔案請求物件來嘍:
package com.example.lxb.hellohttp.httpclient.request; /** * Created by lxb on 2017/4/13. */ public class FileRequest extends Request{ private String filePath; public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } }
三。再來看看直正的幹活類:
方法分發介面,主要為了顆粒度:
/** * 開始執行普通 資料網路請求操作 * * @param requestParams */ public void execute(RequestParams requestParams, MsgHandler response) { this.method = requestParams.getMethod(); this.URL = requestParams.getURL(); Map<String, String> params = requestParams.getParams(); if (!params.isEmpty()) { paramsData = new JSONObject(); Iterator iter = params.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); try { paramsData.put(entry.getKey().toString(), entry.getValue().toString()); } catch (JSONException e) { e.printStackTrace(); } } } request(this.method, this.URL, paramsData.toString(), response); }
/** * 預設請求方法採用POST * * @param method * @param url * @param param * @return */ public void request(String method, String url, String param, MsgHandler response) { if (method.equals(MsgCode.POST)) { doPost(url, param, response); } else if (method.equals(MsgCode.GET)) { doPost(url, param, response); } }
四。http請求方式:Post 與 Get (這兩種方式的區別這裡暫且不提)兩種方式,主要是先設定一些請求格式,然後將http中的輸入/輸出流給拿出來進行相應的操作就行了。
http相對socket協議來說簡單很多,它的資料包格式都已經封裝好了,也就是它有固定的訊息頭
但如果採用socket協議的話,訊息頭就需要自己去定義了,不然很容易出現粘包的情況。
post 方式:程式碼中有註釋,不詳細解釋了
/** * 向指定 URL 傳送POST方法的請求 * * @param url 傳送請求的 URL * @param param 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。 * @return 所代表遠端資源的響應結果 * @throws Exception */ public void doPost(String url, String param, MsgHandler response) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setRequestProperty("charset", "utf-8"); conn.setUseCaches(false); conn.setDoOutput(true); conn.setDoInput(true); conn.setReadTimeout(TIMEOUT_IN_MILLIONS); conn.setConnectTimeout(TIMEOUT_IN_MILLIONS); if (param != null && !param.trim().equals("")) { out = new PrintWriter(conn.getOutputStream()); out.print(param); out.flush(); } if (conn.getResponseCode() == 200) { in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } else { Map<String, String> Failure = new HashMap<>(); Failure.put(MsgCode.Failure_Key, conn.getResponseCode() + ""); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Failure, Failure); } } catch (Exception e) { e.printStackTrace(); Map<String, String> Exception = new HashMap<>(); Exception.put(MsgCode.Exection_Key, e.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Exection, Exception); } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); Map<String, String> Exception = new HashMap<>(); Exception.put(MsgCode.Exection_Key, ex.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Exection, Exception); } } Map<String, String> success = new HashMap<>(); success.put(MsgCode.Success_Key, result); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Success, success); //return result; }
get 方式:
/** * Get請求,獲得返回資料 * * @param urlStr * @return * @throws Exception */ public void doGet(String urlStr, String param, MsgHandler response) { PrintWriter out = null; URL url = null; HttpURLConnection conn = null; InputStream is = null; ByteArrayOutputStream baos = null; try { url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); conn.setUseCaches(false); conn.setDoOutput(true); conn.setDoInput(true); conn.setReadTimeout(TIMEOUT_IN_MILLIONS); conn.setConnectTimeout(TIMEOUT_IN_MILLIONS); conn.setRequestMethod("GET"); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); if (param != null && !param.trim().equals("")) { out = new PrintWriter(conn.getOutputStream()); out.print(param); out.flush(); } if (conn.getResponseCode() == 200) { is = conn.getInputStream(); baos = new ByteArrayOutputStream(); int len = -1; byte[] buf = new byte[1024]; while ((len = is.read(buf)) != -1) { baos.write(buf, 0, len); } baos.flush(); Map<String, String> success = new HashMap<>(); success.put(MsgCode.Success_Key, baos.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Success, success); //return baos.toString(); } else { Map<String, String> Failure = new HashMap<>(); Failure.put(MsgCode.Failure_Key, conn.getResponseCode() + ""); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Failure, Failure); //throw new RuntimeException(" responseCode is not 200 ... "); } } catch (Exception e) { //e.printStackTrace(); Map<String, String> Exception = new HashMap<>(); Exception.put(MsgCode.Exection_Key, e.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Exection, Exception); } finally { try { if (is != null) is.close(); } catch (IOException e) { Map<String, String> Exception = new HashMap<>(); Exception.put(MsgCode.Exection_Key, e.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Exection, Exception); } try { if (baos != null) baos.close(); } catch (IOException e) { Map<String, String> Exception = new HashMap<>(); Exception.put(MsgCode.Exection_Key, e.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Exection, Exception); } conn.disconnect(); } //return null; }
上傳檔案方法:主要是注意檔案請求頭的格式,其他沒什麼了
/** * 上傳檔案時,注意http協議上傳檔案的協議頭部格式 * <p> * 示例:例如向主機192.168.1.8上傳圖片格式如下: * <p> * POST/logsys/home/uploadIspeedLog!doDefault.html HTTP/1.1 * Accept: text/plain, * Accept-Language: zh-cn * Host: 192.168.1.8 * Content-Type:multipart/form-data;boundary=-----------------------------7db372eb000e2 // step 1 * User-Agent: WinHttpClient * Content-Length: 3693 * Connection: Keep-Alive * <p> * -------------------------------7db372eb000e2 //step 2 * Content-Disposition: form-data; name="file"; filename="kn.jpg" //step 3 * Content-Type: image/jpeg * (此處省略jpeg檔案二進位制資料...) * -------------------------------7db372eb000e2-- //step 4 * * @param RequestURL * @param path * @param response * @return */ public boolean uploadFile(String RequestURL, String path, MsgHandler response) { long uploadCount = 0; long totalSize = 0; Map<String, String> Uploadprogress = new HashMap<>(); Uploadprogress.put(MsgCode.Loading_Total_Key, totalSize + ""); File uploadFile = new File(path); if (!uploadFile.exists()) return false; totalSize = uploadFile.length(); String filePostfix = path.substring(path.lastIndexOf("/") + 1, path.length()); String lineEnd = "\r\n"; //嚴格遵循http協議包含換行 String twoHyphens = "--"; //邊界前後必須用--宣告 String boundary = "*****"; //邊界宣告,可自定義 try { URL url = new URL(RequestURL); HttpURLConnection con = (HttpURLConnection) url.openConnection(); /** * 允許Input、Output,不使用Cache */ con.setDoInput(true); con.setDoOutput(true); con.setUseCaches(false); con.setReadTimeout(TIMEOUT_IN_MILLIONS); con.setConnectTimeout(TIMEOUT_IN_MILLIONS); /** * 設定http連線屬性,這個是參照傳輸檔案的頭部資訊來寫的 */ con.setRequestMethod("POST"); con.setRequestProperty("Connection", "Keep-Alive"); con.setRequestProperty("Charset", "UTF-8"); con.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); // step1 DataOutputStream dataOutputStream = new DataOutputStream(con.getOutputStream()); //獲取輸出流通過此處進行檔案上傳 /** * 注意格式 即:頭+邊界+尾,它們的分隔可以自定義,方便分隔一張圖片是否已經上傳完畢 */ dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd); //step 2 dataOutputStream.writeBytes("Content-Disposition: form-data; " + "name=\"file\";filename=\"" + filePostfix + "\"" + lineEnd); //step3 dataOutputStream.writeBytes(lineEnd); //新增換行,每一句格式都須嚴格遵守 /** * 處理檔案 */ FileInputStream fStream = new FileInputStream(path); int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int length = -1; while ((length = fStream.read(buffer)) != -1) { dataOutputStream.write(buffer, 0, length); uploadCount += bufferSize; Uploadprogress.put(MsgCode.Loading_CurProgress_Key, ((uploadCount * 100) / totalSize) + ""); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Loading, Uploadprogress); //System.out.println("369-----------------已上傳:"+((uploadCount * 100) / totalSize)); } /** * 寫入檔案流時,再寫入一遍檔案尾,告訴伺服器我檔案流上傳完成可以讀取了 */ dataOutputStream.writeBytes(lineEnd); dataOutputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); fStream.close(); dataOutputStream.flush(); /* 取得Response內容 */ /* InputStream is = con.getInputStream(); int ch; StringBuffer b = new StringBuffer(); while ((ch = is.read()) != -1) { b.append((char) ch); }*/ dataOutputStream.close(); int nResponseCode = con.getResponseCode(); if (nResponseCode == 200) { Map<String, String> success = new HashMap<>(); success.put(MsgCode.Success_Key, path); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Success, success); return true; } else { return false; } } catch (Exception e) { System.out.println("上傳失敗:" + e); Map<String, String> Exception = new HashMap<>(); Exception.put(MsgCode.Exection_Key, e.toString()); ThreadCrossHandler.sendMsgByHandler(response, MsgCode.Exection, Exception); return false; } }
下載檔案:
/** * 下載檔案,並把檔案儲存在制定目錄(-1:下載失敗,0:下載成功,1:檔案已存在) * * @param urlStr * @param path * @param fileName * @param response * @return */ public int downloadFiles(String urlStr, String path, String fileName, MsgHandler response) { try { FileUtils fileUtils = FileUtils.getInstance(); if (fileUtils.isFileExist(fileName, path)) return 1; else { InputStream inputStream = getInputStreamFromUrl(urlStr); int fileSize = getInputStreamSizeFromUrl(urlStr); File resultFile = fileUtils.write2SDFromInput(fileSize, fileName, path, inputStream, response); if (resultFile == null) return -1; } } catch (Exception e) { System.out.println("讀寫資料異常:" + e); return -1; } return 0; }
/** * 通過url獲取輸入流 * * @param urlStr * @return * @throws IOException */ public InputStream getInputStreamFromUrl(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); InputStream inputStream = urlConn.getInputStream(); return inputStream; } /** * 獲取輸入流中檔案大小 * * @param urlStr * @return * @throws IOException */ private int getInputStreamSizeFromUrl(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); return urlConn.getContentLength(); }
請求代理類:
package com.example.lxb.hellohttp.httpclient; import com.example.lxb.hellohttp.httpclient.client.FileReuqestThread; import com.example.lxb.hellohttp.httpclient.client.RequestThread; import com.example.lxb.hellohttp.httpclient.handler.MsgHandler; import com.example.lxb.hellohttp.httpclient.request.FileRequest; import com.example.lxb.hellohttp.httpclient.request.RequestParams; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** * 網編請求的代理類 * <p> * Created by lxb on 2017/4/11. */ public class HelloHttp extends HttpClient { private static HelloHttp helloHttp; private RequestParams requestParams; private FileRequest fileRequest; private MsgHandler response相關推薦
android 設計一個簡易的Http網路請求框架
一.開發初衷:最近專案中需要用到版本升級這一塊,需要用到一些基本的資料請求與檔案下載功能。之前做專案都是用別人的網路框架,類似retrofit 、 okhttp、 fresco等框架,用的多了,發現這幾個網路請求框架,無非都是 按解決以下幾個問題為導向的: 1
Android http網路請求框架搭建
android上網路請求大致分為使用socket和http,普通應用大多使用http或者https,今天主要介紹http,實現目標通過使用http搭建一套簡單的Android網路請求框架。 網路請求部分: Android的網路請求部分我們大致分為: 引數傳遞,網路請求,
Android 9.0/P http 網路請求的問題
Google表示,為保證使用者資料和裝置的安全,針對下一代 Android 系統(Android P) 的應用程式,將要求預設使用加密連線,這意味著 Android P 將禁止 App 使用所有未加密的連線,因此執行 Android P 系統的安卓裝置無論是接收或者傳送流量,未來都不能明碼傳輸,需要使用下一代
學習 :Android 9.0/P http 網路請求的問題
今天在網上看文章的時候看到了這一篇文章,說是Android9.0網路請求會有問題,然後好奇就仔細看了一下。看完之後感覺對自己以後可能有用處就寫這篇文章來記錄一下。以後用到的話可以拿來用一下。 為什麼特意注意了一下這篇文章呢,因為我們公司的請求一直用的是Http而不是用的Https,所以感覺以後升
Android打造一個通用的網路請求引擎HttpUtils
打造一個通用的網路請求引擎HttpUtils 為什麼要打造這個引擎 Xutils的引擎 Okhttp的引擎 使用 為什麼要打造這個引擎 自Android 6.0之後,HttpClient被廢,好多APP是不是出現蛋疼的事,趕緊換掉網路請求
自己動手寫一個輕量級的Android網路請求框架
最近有空在看《App研發錄》一書,良心之作。書中第一部分第二章節講了不少關於網路底層封裝的知識,看後覺得學到了不少乾貨。 索性自己也動手完成了一個非常輕量級的網路請求框架,從該書中獲得了不少幫助。特此記錄,回顧一下思路,整理收穫。OK,一起來看。 就如書中所
給Android封裝的一個簡單網路請求框架
最近做畢業設計,沒有用volley框架或則自己以前做專案比較熟悉的beeframework框架的網路請求部分(不得讚一句beeframework的網路請求部分封裝得很好,可以研究一下然後自己仿照著寫寫),本著熟悉和總結andorid一些基礎知識的目的,自己試著寫了一個自己在
Android網路請求框架Retrofit使用介紹
前言 在android開發中,網路請求是最常用的操作之一,目前熱門的網路請求框架有:Retrofit、volley、okhttp、Android-Async-Http,這裡公司專案中用到Retrofit,之前沒了解過,這裡做個學習記錄。 本文參考博文:這是一份很詳細的 Retrofit 2.
Android中retrofit網路請求框架使用
Retrofit 是 Square 公司出品的 HTTP 請求庫, 同時是 Square 是最早開源專案之一, Retrofit 是目前 Android 最流行的 H
Okhttp3網路請求框架+MVP設計模式簡單實戰
Okhttp 目前最新版本的是okhttp:3.4.1,也稱為Okhttp3。 OkHttp是一個精巧的網路請求庫,不僅在介面封裝做的簡單易用,在底層實現上也自成一派。比起原生的HttpURLConnection有過之而無不及,現在已經成為廣大開發者的首選網路通訊庫。 特性 支援ht
Android-volley淺談-從原始碼去了解它為什麼是Google推薦的網路請求框架
今天想總結的是Volley這個網路請求框架,雖然volley論火爆程度比不上okhttp和retrofit 這兩個,並且在日常使用的過程中可能很少有人能去深究為什麼volley是Google 所推薦的網路框架,不管從使用還是從原始碼去理解,我覺得volley都是一款值得去深究
Retrofit 2.0使用詳解,配合OkHttp、Gson,Android最強網路請求框架
1.使用retrofit,需要下載一些jar包 2.介紹這些jar包的作用 在1.x版本的retrofit框架: 只需要Retrofit包和gson-2.4.jar包就行了,那時的Retrofit預設是使用okhttp jar包來網路請
Android網路請求框架Retrofit使用詳解
前言 技術日新月異,一天不跑,就out了! 早點的Android的網路請求框架android-async-http,Volley,XUtils早已被拋諸腦後,到前段時間的OKHttp,再到最近一段時間大火的Retrofit,封裝的越來越好!程式碼越來越簡潔!
深入淺出的理解Android網路請求框架
以前在寫網路請求的時候,我們經常乾的一件事情就是首先開啟一個執行緒,然後建立一個Handler將網路請求的操作放到一個執行緒中去執行。因為大家都知道網路請求是一個耗時的操作,不能放在主執行緒中去執行,但是我們發現我們用框架去處理網路請求的時候,程式碼量確
Android知識點之網路底層封裝:細數常用的網路請求框架
Android知識體系更新如此之快,讓人一不小心就會感覺,額,我out了,翻看之前的文章,14年、15年大家討論的網路底層框架都是HttpURLConnection,HttpClient,細數二
Android 網路請求框架 Retrofit2.0實踐使用總結
比較AsyncHttpClient 、Volley、Retrofit三者的請求時間 使用 單次請求 7個請求 25個請求 AsyncHttpClient 941ms 4539ms 13957ms Volley
Android肝帝戰紀之網路請求框架封裝(Retrofit的封裝)
網路請求框架封裝(OkHttp3+Retrofit+loading的封裝) Retrofit的Github連結 點此連結到Github AVLoadingIndicatorView的Github連結(封裝到請求框架中,請求過程中的loading樣式框(
Android 3.0 http網路請求
Android 3.0以上使用http網路請求需要加入如下程式碼: StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().d
OkHttp3-Android網路請求框架常用用法介紹與例項(mob請求天氣預報)
前言: OkHttp是Square開發的第三方庫,用於傳送和接收基於HTTP的網路請求。它建立在Okio庫之上,通過建立共享記憶體池,它嘗試通過標準Java I / O庫更高效地讀取和寫入資料。它還是Retrofit庫的底層庫,為使用基於REST的AP
如何獨立開發一個網路請求框架
1 package com.lghsaleimage; 2 3 import android.graphics.Bitmap; 4 import android.os.Handler; 5 import android.os.Message; 6 import andr