OKHttp是一個處理網絡請求的開源項目,Android 當前最火熱網絡框架,由移動支付Square公司貢獻,用於替代HttpUrlConnection和Apache HttpClient(android API23 6.0裏已移除HttpClient)。
OKHttpGitHub地址css
OKHttp優勢html
OKHttp的功能java
主要介紹 OkHttp3 的 Get 請求、 Post 請求、 上傳下載文件 、 上傳下載圖片等功能 。android
添加OkHttp3的依賴git
添加網絡權限github
<uses-permission android:name="android.permission.INTERNET"/>
添加請求頭
private Request.Builder addHeaders() { Request.Builder builder = new Request.Builder() //addHeader,可添加多個請求頭 header,惟一,會覆蓋 .addHeader("Connection", "keep-alive") .addHeader("platform", "2") .addHeader("phoneModel", Build.MODEL) .addHeader("systemVersion", Build.VERSION.RELEASE) .addHeader("appVersion", "3.2.0") .header("sid", "eyJhZGRDaGFubmVsIjoiYXBwIiwiYWRkUHJvZHVjdCI6InFia3BsdXMiLCJhZGRUaW1lIjoxNTAzOTk1NDQxOTEzLCJyb2xlIjoiUk9MRV9VU0VSIiwidXBkYXRlVGltZSI6MTUwMzk5NTQ0MTkxMywidXNlcklkIjoxNjQxMTQ3fQ==.b0e5fd6266ab475919ee810a82028c0ddce3f5a0e1faf5b5e423fb2aaf05ffbf"); return builder; }
1.異步GET請求編程
//1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //2.建立Request對象,設置一個url地址(百度地址),設置請求方式。 Request request = new Request.Builder().url("http://www.baidu.com").method("GET",null).build(); //3.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //4.請求加入調度,重寫回調方法 call.enqueue(new Callback() { //請求失敗執行的方法 @Override public void onFailure(Call call, IOException e) { } //請求成功執行的方法 @Override public void onResponse(Call call, Response response) throws IOException { } });
上面就是發送一個異步GET請求的4個步驟:json
注意事項:api
2.同步GET請求數組
//1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //2.建立Request對象,設置一個url地址(百度地址),設置請求方式。 Request request = new Request.Builder().url("http://www.baidu.com").method("GET",null).build(); //3.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //4.同步調用會阻塞主線程,這邊在子線程進行 new Thread(new Runnable() { @Override public void run() { try { //同步調用,返回Response,會拋出IO異常 Response response = call.execute(); } catch (IOException e) { e.printStackTrace(); } } }).start();
同步GET請求和異步GET請求基本同樣,不一樣地方是同步請求調用Call的execute()方法,而異步請求調用call.enqueue()方法(具體2個方法的不一樣點我下一遍具體源碼詳解再說)。
3.POST請求提交鍵值對
//1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //2.經過new FormBody()調用build方法,建立一個RequestBody,能夠用add添加鍵值對 RequestBody requestBody = new FormBody.Builder().add("name","zhangqilu").add("age","25").build(); //3.建立Request對象,設置URL地址,將RequestBody做爲post方法的參數傳入 Request request = new Request.Builder().url("url").post(requestBody).build(); //4.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //5.請求加入調度,重寫回調方法 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
上面就是一個異步POST請求提交鍵值對的5個步驟:
經過對比咱們發現異步的POST請求和GET請求步驟很類似。
4.異步POST請求提交字符串
POST請求提交字符串和POST請求提交鍵值對很是類似,不一樣地方主要是RequestBody,下面咱們來具體看一下。
在有些狀況下客戶端須要向服務端傳送字符串,咱們該怎麼作?
咱們須要用到另外一種方式來構造一個 RequestBody 以下所示:
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");//"類型,字節碼" //字符串 String value = "{username:admin;password:admin}"; //1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //2.經過RequestBody.create 建立requestBody對象 RequestBody requestBody =RequestBody.create(mediaType, value); //3.建立Request對象,設置URL地址,將RequestBody做爲post方法的參數傳入 Request request = new Request.Builder().url("url").post(requestBody).build(); //4.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //5.請求加入調度,重寫回調方法 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
5.異步POST請求上傳文件
咱們這裏舉一個上傳圖片的例子,也能夠是其餘文件如,TXT文檔等,不一樣地方主要是RequestBody,首先咱們要添加存儲卡讀寫權限,在 AndroidManifest.xml 文件中添加以下代碼:
下面咱們具體看一下上傳文件代碼。
//1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //上傳的圖片 File file = new File(Environment.getExternalStorageDirectory(), "zhuangqilu.png"); //2.經過RequestBody.create 建立requestBody對象,application/octet-stream 表示文件是任意二進制數據流 RequestBody requestBody =RequestBody.create(MediaType.parse("application/octet-stream"), file); //3.建立Request對象,設置URL地址,將RequestBody做爲post方法的參數傳入 Request request = new Request.Builder().url("url").post(requestBody).build(); //4.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //5.請求加入調度,重寫回調方法 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
6.異步GET請求下載文件
下載文件也是咱們常常用到的功能,咱們就舉個下載圖片的例子吧
//1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //2.建立Request對象,設置一個url地址(百度地址),設置請求方式。 Request request = new Request.Builder().url("https://www.baidu.com/img/bd_logo1.png").get().build(); //3.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //4.請求加入調度,重寫回調方法 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "onFailure: "+call.toString() ); } @Override public void onResponse(Call call, Response response) throws IOException { //拿到字節流 InputStream is = response.body().byteStream(); int len = 0; //設置下載圖片存儲路徑和名稱 File file = new File(Environment.getExternalStorageDirectory(),"baidu.png"); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[128]; while((len = is.read(buf))!= -1){ fos.write(buf,0,len); Log.e(TAG, "onResponse: "+len ); } fos.flush(); fos.close(); is.close(); } });
Get請求下載文件仍是比較簡單,設置下載地址,在回調函數中拿到了圖片的字節流,而後保存爲了本地的一張圖片。
從網絡下載一張圖片並直接設置到ImageView中。
@Override public void onResponse(Call call, Response response) throws IOException { InputStream is = response.body().byteStream(); //使用 BitmapFactory 的 decodeStream 將圖片的輸入流直接轉換爲 Bitmap final Bitmap bitmap = BitmapFactory.decodeStream(is); //在主線程中操做UI runOnUiThread(new Runnable() { @Override public void run() { //而後將Bitmap設置到 ImageView 中 imageView.setImageBitmap(bitmap); } }); is.close();
主要註釋已在代碼中了。
7.異步POST請求上傳Multipart文件
咱們在有些狀況下既要上傳文件還要上傳其餘類型字段。好比在我的中心咱們能夠修更名字,年齡,修改圖像,這其實就是一個表單。這裏咱們用到MuiltipartBody ,它 是RequestBody 的一個子類,咱們提交表單就是利用這個類來構建一個 RequestBody,咱們來看一下具體代碼。
//1.建立OkHttpClient對象 OkHttpClient okHttpClient = new OkHttpClient(); //上傳的圖片 File file = new File(Environment.getExternalStorageDirectory(), "zhuangqilu.png"); //2.經過new MultipartBody build() 建立requestBody對象, RequestBody requestBody = new MultipartBody.Builder() //設置類型是表單 .setType(MultipartBody.FORM) //添加數據 .addFormDataPart("username","zhangqilu") .addFormDataPart("age","25") .addFormDataPart("image","zhangqilu.png", RequestBody.create(MediaType.parse("image/png"),file)) .build(); //3.建立Request對象,設置URL地址,將RequestBody做爲post方法的參數傳入 Request request = new Request.Builder().url("url").post(requestBody).build(); //4.建立一個call對象,參數就是Request請求對象 Call call = okHttpClient.newCall(request); //5.請求加入調度,重寫回調方法 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
public void postForm(View view) { OkHttpClient client = new OkHttpClient(); RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("username", "葉應是葉") .addFormDataPart("password", "葉應是葉") .build(); final Request request = new Request.Builder() .url("http://www.jianshu.com/") .post(requestBody) .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { ToastUtil.showToast(PostFormActivity.this, "Post Form 失敗"); } @Override public void onResponse(Call call, Response response) throws IOException { final String responseStr = response.body().string(); ToastUtil.showToast(PostFormActivity.this, "Code:" + String.valueOf(response.code())); runOnUiThread(new Runnable() { @Override public void run() { tv_result.setText(responseStr); } }); } }); }
public void postStreaming(View view) { final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8"); File file = new File("README.md"); final FileInputStream fileInputStream1=new FileInputStream(file); RequestBody requestBody1=new RequestBody() { @Nullable @Override public MediaType contentType() { return MEDIA_TYPE_MARKDOWN; } @Override public void writeTo(BufferedSink sink) throws IOException { OutputStream outputStream=sink.outputStream(); int length; byte[] buffer = new byte[1024]; while ((length = fileInputStream1.read(buffer)) != -1) { outputStream.write(buffer, 0, length); } } }; RequestBody requestBody2=new RequestBody() { @Nullable @Override public MediaType contentType() { return MEDIA_TYPE_MARKDOWN; } @Override public void writeTo(BufferedSink sink) throws IOException { int length; byte[] buffer = new byte[1024]; while ((length = fileInputStream1.read(buffer)) != -1) { sink.write(buffer, 0, length); } } }; Request request = new Request.Builder() .url(url) .post(requestBody1) .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { ToastUtil.showToast(PostStreamingActivity.this, "Post Streaming 失敗"); } @Override public void onResponse(Call call, Response response) throws IOException { final String responseStr = response.body().string(); ToastUtil.showToast(PostStreamingActivity.this, "Code:" + String.valueOf(response.code())); runOnUiThread(new Runnable() { @Override public void run() { tv_result.setText(responseStr); } }); } }); }
這裏來經過Gson將response的內容解析爲Java Bean
首先須要先去將Gson.jar文件導入工程
這裏來經過OkHttp訪問接口「http://news-at.zhihu.com/api/4/themes」,獲取Json數據而後將之解析爲JavaBean實體
Response response = okHttpClient.newCall(request).execute(); if (response.isSuccessful()){ User user = new Gson().fromJson(response.body().charStream(), User.class); }
和OkHttp2.x有區別的是不能經過OkHttpClient直接設置超時時間和緩存了,而是經過OkHttpClient.Builder來設置,經過builder配置好OkHttpClient後用builder.build()來返回OkHttpClient,因此咱們一般不會調用new OkHttpClient()來獲得OkHttpClient,而是經過builder.build():
File sdcache = getExternalCacheDir(); int cacheSize = 10 * 1024 * 1024; OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout(15, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize)); OkHttpClient mOkHttpClient=builder.build();
取消請求仍舊能夠調用call.cancel(),這個沒有變化,不明白的能夠查看上一篇文章Android網絡編程(五)OkHttp2.x用法全解析,這裏就不贅述了,封裝上一篇也講過仍舊推薦OkHttpFinal,它目前是基於OkHttp3來進行封裝的。
call.cancel();//取消請求,不能取消已經準備完成的請求 okHttpClient.dispatcher().cancelAll();//取消全部請求
有時候網絡條件很差的狀況下,用戶會主動關閉頁面,這時候須要取消正在請求的http request, OkHttp提供了cancel方法,可是實際在使用過程當中發現,若是調用cancel()方法,會回調到CallBack裏面的 onFailure方法中,
/** * Called when the request could not be executed due to cancellation, a connectivity problem or * timeout. Because networks can fail during an exchange, it is possible that the remote server * accepted the request before the failure. */ void onFailure(Call call, IOException e);
能夠看到註釋,當取消一個請求,網絡鏈接錯誤,或者超時都會回調到這個方法中來,可是我想對取消請求作一下單獨處理,這個時候就須要區分不一樣的失敗類型了
測試發現不一樣的失敗類型返回的IOException e 不同,因此能夠經過e.toString 中的關鍵字來區分不一樣的錯誤類型
本身主動取消的錯誤的 java.net.SocketException: Socket closed 超時的錯誤是 java.net.SocketTimeoutException 網絡出錯的錯誤是java.net.ConnectException: Failed to connect to xxxxx
直貼了部分代碼
call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { if(e.toString().contains("closed")) { //若是是主動取消的狀況下 }else{ //其餘狀況下 }
添加Interceptor
// 配置一些信息進入OkHttpClient mOkHttpClient = new OkHttpClient().newBuilder() .connectTimeout(REQUEST_TIME, TimeUnit.SECONDS) .readTimeout(REQUEST_TIME, TimeUnit.SECONDS) .writeTimeout(REQUEST_TIME, TimeUnit.SECONDS) .addInterceptor(new LoggerInterceptor()) .build();
只要利用addInterceptor方法就能夠添加攔截器,而自定義的攔截器只須要實現 Interceptor 接口就好了,可使用攔截器方便的打印網絡請求時,須要查看的日誌。以下所示:
public class LoggerInterceptor implements Interceptor { @Override public Response intercept(@NonNull Chain chain) throws IOException { // 攔截請求,獲取到該次請求的request Request request = chain.request(); // 執行本次網絡請求操做,返回response信息 Response response = chain.proceed(request); if (Configuration.DEBUG) { for (String key : request.headers().toMultimap().keySet()) { LogUtil.e("zp_test", "header: {" + key + " : " + request.headers().toMultimap().get(key) + "}"); } LogUtil.e("zp_test", "url: " + request.url().uri().toString()); ResponseBody responseBody = response.body(); if (HttpHeaders.hasBody(response) && responseBody != null) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(responseBody.byteStream(), "utf-8")); String result; while ((result = bufferedReader.readLine()) != null) { LogUtil.e("zp_test", "response: " + result); } // 測試代碼 responseBody.string(); } } // 注意,這樣寫,等於從新建立Request,獲取新的Response,避免在執行以上代碼時, // 調用了responseBody.string()而不能在返回體中再次調用。 return response.newBuilder().build(); } }
注意事項
setType(MultipartBody.FORM)
from:https://www.cnblogs.com/chenxibobo/p/9585760.html