簡介
okhttp是一個第三方類庫,用於android中請求網絡。這是一個開源項目,是安卓端最火熱的輕量級框架,由移動支付Square公司貢獻(該公司還貢獻了Picasso和LeakCanary) 。用於替代HttpUrlConnection和Apache HttpClient(android API23 里已移除HttpClient)。
- 官網網址:OKHttp官網
- OkHttp github地址:https://github.com/square/okhttp
- OkHttp jar包下載: https://search.maven.org/remote_content?g=com.squareup.okhttp3&a=okhttp&v=LATEST
- 參考博客:https://blog.csdn.net/lmj623565791/article/details/47911083
依賴
implementation 'com.squareup.okhttp3:okhttp:4.4.0'
同步請求方式(get請求)
步驟一 創建請求接口網址
我使用了http://www.sosoapi.com/ 來創建了一個訪問接口,用來驗證OkHttp是否請求成功。如果你有興趣了解,可以直接進入網站,里面有詳細的demo演示。后續我將不在贅述這段。
我在請求響應里添加了一段JSON數據:
[ { "name": "get測試", "content": "你成功獲取了數據" } ]
步驟二 創建JSON解析方法
這里我寫一下解析JSON數據的方法,來解析get或者post得到的JSON數據。后續我將不在贅述這段。
/** * JSON 解析方法 * @param jsonData * @return */ public String readJSONContent(String jsonData){ try { StringBuffer sb = new StringBuffer(); JSONArray jsonArray = new JSONArray(jsonData); for (int i=0;i<jsonArray.length();i++){ JSONObject jsonObject = jsonArray.getJSONObject(i); sb.append(jsonObject.getString("name")+"\n"); sb.append(jsonObject.getString("content")+"\n"); } return sb.toString(); } catch (JSONException e) { Log.e("JSONException錯誤", "readContent: "+e.toString()); return e.toString(); } }
步驟三 創建OkHttp 同步請求
/** * 同步請求 */ public void synchro(){ Thread thread = new Thread(new Runnable() { @Override public void run() { OkHttpClient okHttpClient = new OkHttpClient();//創建單例 Request request = new Request.Builder()//創建請求 .url("http://www.sosoapi.com/pass/mock/12003/test/gettest") .build(); try { Response response = okHttpClient.newCall(request).execute();//執行請求 mContent = response.body().string();//得到返回響應,注意response.body().string() 只能調用一次! runOnUiThread(new Runnable() { @Override public void run() { mtextView.setText(readJSONContent(mContent)); } }); } catch (IOException e) { e.printStackTrace(); Log.e("OkHttpActivity", e.toString() ); } } }); thread.start(); }
注意!response.body().string() 只能調用一次! 因為響應主體 RessponseBody
持有的資源可能會很大,所以 OkHttp 並不會將其直接保存到內存中,只是持有數據流連接。只有當我們需要時,才會從服務器獲取數據並返回。同時,考慮到應用重復讀取數據的可能性很小,所以將其設計為一次性流(one-shot)
,讀取后即 '關閉並釋放資源'。
效果圖:
異步請求方式(get請求)
注意1:異步請求方式,請求的回調會在子線程里,所以如果需要更新UI你需要切換到主線程。且你不需要在new 一個線程包裹這個異步請求了。另外如何切換到主線程請使用 Handler 例子:
注意2:在異步請求方法里,請不要將 public void onResponse(Call call, final Response response)回調里的response回調數據放到UI線程里解析,因為有一個天坑,有可能在UI線程里解析的時候response里面卻還沒有塞入數據(我也覺得很神奇,不知道寫okhttp的公司是怎么想的,為什么不處理完所有數據在提供回調)
private Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() { @Override public void run() { //主線程 } });
創建異步請求
/** * get異步請求 */ public void asynchronous(){ OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder() .url("http://www.sosoapi.com/pass/mock/12003/test/gettest") .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //失敗回調 } @Override public void onResponse(Call call, final Response response) throws IOException {
mContent = readJSONContent(response.body().string());//注意response.body().string() 只能調用一次! //響應成功,這個回調在子線程中,所以不需要創建線程 if (response.isSuccessful()){ //isSuccessful方法:如果代碼位於(200…300)中,則返回true,這意味着請求已成功接收 runOnUiThread(new Runnable() { @Override public void run() { try { //因為在子線程,所以我們需要回到主線程中更新UI數據 mtextView.setText(mContent); } catch (IOException e) { e.printStackTrace(); } } }); } } }); }
Post請求
post請求也同時有同步與異步方法,與上面一致,所以這里就不展示了,下面我們來看看post請求發送
/** * post請求 */ public void post(){ new Thread(new Runnable() { @Override public void run() { //實例 OkHttpClient okHttpClient = new OkHttpClient(); //創建post請求數據表單 RequestBody requestBody = new FormBody.Builder() .add("name","請求post") .add("password","123456") .build(); //創建請求 final Request request = new Request.Builder() .url("http://www.sosoapi.com/pass/mock/12003/test/posttest") .post(requestBody)//添加post請求 .build(); try { //發送請求得到響應 final Response response = okHttpClient.newCall(request).execute(); runOnUiThread(new Runnable() { @Override public void run() { try { mtextView.setText(readJSONContent(response.body().string()));//注意response.body().string() 只能調用一次! } catch (IOException e) { e.printStackTrace(); } } }); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
效果圖:
取消網絡請求
取消okhttp的網絡請求很簡單只需要
call.cancel();
如何鑒別是網絡異常還是主動取消,請求取消的回調在onFailure(Call call, IOException e)里回調,這個時候我們需要再次判斷onFailure里回調的是我們自己主動取消的還是網絡異常報錯的
mCall.enqueue(new Callback() {//發送請求 @Override public void onFailure(final Call call, final IOException e) { if (call.isCanceled()){ listener.onCancel(); }else { listener.onFailure(call, e); } } //省略下面代碼...
RequestBody創建
RequestBody是okhttp post發送數據配置類.在這之前我們先了解下回顧一下body類型
- application/x-www-form-urlencoded 表單數據
- multipart/form-data 表單文件上傳
- application/json 序列化JSON數據
- text/xml XML數據
這些body類型需要在http header頭部就寫上,但是okhttp不需要我們手動在header寫上類型了.okhttp提供了FormBody和MultipartBody的類型,方便你的快速創建 application/x-www-form-urlencoded 與 multipart/form-data
FormBody 創建方式
/** * 此body是 默認application/x-www-form-urlencoded,你可以進入FormBody類查看,第一行靜態常量就是這個 */ public void FormBody(){ FormBody.Builder builder = new FormBody.Builder(); builder.add("key","content"); builder.build(); }
MultipartBody 創建方式
/** * multipart/form-data */ public void MultipartBody(){ RequestBody requestBody = new RequestBody() { @Override public MediaType contentType() { return null; } @Override public void writeTo(BufferedSink sink) throws IOException { //上傳流 sink.write() } }; MultipartBody.Builder builder = new MultipartBody.Builder(); builder.addFormDataPart("key","content");//表單數據 builder.addFormDataPart("file_key","/path/image.jpg",requestBody);//文件數據 MultipartBody multipartBody = builder.build(); }
手動創建4種body
當然,有特殊需求你還可以添加手動其他body類型,create也支持好幾種數據的上傳形式
/** * 手動創建4種body */ public void RequestBody(){ RequestBody applicationFormUrlencodedBody = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded; charset=utf-8"),new String("demo").getBytes()); RequestBody multipartBody = RequestBody.create(MediaType.parse("multipart/form-data; charset=utf-8"),new File("/path/image.jpg")); RequestBody jsonBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),"content"); RequestBody textbody = RequestBody.create(MediaType.parse("text/xml; charset=utf-8"),"content"); }
實現OkHttpClient客戶端單例模式
public class OkHttpClientCreate { private static final boolean IS_RETRY = false;//失敗是否重連 private static final int CONNECT_TIME = 10;//設置連接超時時間 單位:秒 private static final int READ_TIME = 10;//設置讀取超時時間 private static final int WRITE_TIME = 10;//設置寫入超時間 private static OkHttpClient mOkHttpClient; public static OkHttpClient CreateClient(){ if (mOkHttpClient == null){ return mOkHttpClient = new OkHttpClient.Builder() .retryOnConnectionFailure(IS_RETRY) .connectTimeout(CONNECT_TIME,TimeUnit.SECONDS)//連接超時 .readTimeout(READ_TIME,TimeUnit.SECONDS)//讀取超時 .writeTimeout(WRITE_TIME,TimeUnit.SECONDS)//寫入超時 // .callTimeout()//呼叫超時,設置此參數為整體流程請求的超時時間 // .addInterceptor() //設置攔截器 // .authenticator() //設置認證器 // .proxy()//設置代理 .build(); } return mOkHttpClient; } public static void destroy(){ mOkHttpClient = null; } }