一.OkHttp的介紹和基本用法
OkHttp是一個流行的開源Java和Android應用程式的HTTP使用者端。它由Square Inc.開發,提供了一種簡單高效的方式來進行應用程式中的HTTP請求。要在Java或Android專案中使用OkHttp,您需要將OkHttp依賴項新增到您的build.gradle檔案中。然後,您可以建立一個OkHttpClient範例,並使用它來進行HTTP請求。OkHttp提供了各種類和方法,用於構建和執行請求、處理響應。使用OkHttp的時候,需要引入:implementation 'com.squareup.okhttp3:okhttp:4.10.0',別忘了新增網路許可權!
由於在進行網路請求的時候,我們主要用到get和post兩種方式,下面就以這兩個為例進行程式碼展示。
1.Get方式:GET請求將引數附加在URL的查詢字串中,即在URL後面使用?
符號連線引數鍵值對。get方式中又可以分為兩種情況,分別是同步請求和非同步請求;同步請求在進行請求的時候,當前執行緒會阻塞住,直到得到伺服器的響應後,後面的程式碼才會執行;而非同步請求不會阻塞當前執行緒,它採用了回撥的方式,請求是在另一個執行緒中執行的,不會影響當前的執行緒。下面給出程式碼:
public void getSync(){//同步請求 new Thread(new Runnable() { @Override public void run() { OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Request.Builder() .url("https://www.httpbin.org/get?a=1&b=2") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); try { Response response = call.execute(); Log.i("getSync",response.body().string()); } catch (IOException e) { e.printStackTrace(); } } }).start(); } public void getAsync(){//非同步請求 OkHttpClient okHttpClient=new OkHttpClient(); Request request=new Request.Builder() .url("https://www.httpbin.org/get?a=1&b=2") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); //非同步請求 call.enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { if(response.isSuccessful()){ Log.i("getAsync",response.body().string()); } } }); }
2.Post方式:POST請求將引數放在請求的主體中,不會直接顯示在URL中。Post請求也分為同步和非同步方式,和get方式用法相同,程式碼如下:
public void postSync(){//同步請求 new Thread(new Runnable() { @Override public void run() { OkHttpClient okHttpClient=new OkHttpClient(); FormBody formBody=new FormBody.Builder() .add("a","1") .add("b","2") .build(); Request request=new Request.Builder() .post(formBody) .url("https://www.httpbin.org/post") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); try { Response response = call.execute(); Log.i("postSync",response.body().string()); } catch (IOException e) { e.printStackTrace(); } } }).start(); } public void postAsync(){//非同步請求 OkHttpClient okHttpClient=new OkHttpClient(); FormBody formBody=new FormBody.Builder() .add("a","1") .add("b","2") .build(); Request request=new Request.Builder() .post(formBody) .url("https://www.httpbin.org/post") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { if(response.isSuccessful()){ Log.i("postAsync",response.body().string()); } } }); }
上面是通過表單的方式將資料提交給伺服器,那如果要上傳檔案給伺服器呢?使用Multipart。
//提交多個檔案給伺服器 public void postFiles(){ OkHttpClient okHttpClient=new OkHttpClient(); File file1=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath()+File.separator+"a.jpg"); File file2=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath()+File.separator+"b.jpg"); RequestBody requestBody1=RequestBody.create(file1, MediaType.parse("application/x-jpg")); RequestBody requestBody2=RequestBody.create(file2, MediaType.parse("application/x-jpg")); MultipartBody multipartBody=new MultipartBody.Builder() .addFormDataPart("a.jpg",file1.getName(),requestBody1) .addFormDataPart("b.jpg",file2.getName(),requestBody2) .build(); Request request=new Request.Builder() .post(multipartBody) .url("https://www.httpbin.org/post") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { if(response.isSuccessful()){ Log.i("postFiles",response.body().string()); } } }); }
如果要檢視各個檔案型別所對應的Content-type字串,可以存取以下這個網址:https://www.runoob.com/http/http-content-type.html
提交Json字串給伺服器:
//提交json資料 public void postJson(){ OkHttpClient okHttpClient=new OkHttpClient(); RequestBody requestBody=RequestBody.create("{\"a\":1,\"b\":2}",MediaType.parse("application/json"));//記得使用跳脫字元處理內部的雙引號 Request request=new Request.Builder() .post(requestBody) .url("https://www.httpbin.org/post") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { if(response.isSuccessful()){ Log.i("postJson",response.body().string()); } } }); }
3.攔截器的使用:OkHttp的攔截器(Interceptors)提供了強大的自定義和修改HTTP請求和響應的能力。攔截器允許在傳送請求前、收到響應後以及其他階段對HTTP流量進行攔截和處理。例如:攔截器可以修改請求的URL、請求方法、請求頭部、請求體等。這對於新增身份驗證頭、設定快取控制頭等場景很有用。用法如下:
public void interceptor(){ OkHttpClient okHttpClient=new OkHttpClient.Builder()//新增攔截器的使用OkHttpClient的內部類Builder .addInterceptor(new Interceptor() {//使用攔截器可以對所有的請求進行統一處理,而不必每個request單獨去處理 @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException { //前置處理,以proceed方法為分割線:提交請求前 Request request = chain.request().newBuilder() .addHeader("id", "first request") .build(); Response response = chain.proceed(request); //後置處理:收到響應後 return response; } }) .addNetworkInterceptor(new Interceptor() {//這個在Interceptor的後面執行,無論新增順序如何 @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException { Log.i("id",chain.request().header("id")); return chain.proceed(chain.request()); } }) .cache(new Cache(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath()+"/cache"),1024*1024))//新增快取 .build(); Request request=new Request.Builder() .url("https://www.httpbin.org/get?a=1&b=2") .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); //非同步請求 call.enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { } @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { if(response.isSuccessful()){ Log.i("interceptor",response.body().string()); } } }); }
4.Cookie的使用:大家應該有這樣的經歷,就是有些網站的好多功能都需要使用者登入之後才能存取,而這個功能可以用cookie實現,在使用者端登入之後,伺服器給使用者端傳送一個cookie,由使用者端儲存;然後客服端在存取需要登入之後才能存取的功能時,只要攜帶這個cookie,伺服器就可以識別該使用者是否登入。用法如下:
public void cookie(){ Map<String,List<Cookie>> cookies=new HashMap<>(); new Thread(new Runnable() { @Override public void run() { OkHttpClient okHttpClient=new OkHttpClient.Builder() .cookieJar(new CookieJar() { @Override public void saveFromResponse(@NonNull HttpUrl httpUrl, @NonNull List<Cookie> list) {//儲存伺服器傳送過來的cookie cookies.put("cookies",list); } @NonNull @Override public List<Cookie> loadForRequest(@NonNull HttpUrl httpUrl) {//請求的時候攜帶cookie if(httpUrl.equals("www.wanandroid.com")){ return cookies.get("cookies"); } return new ArrayList<>(); } }) .build(); FormBody formBody=new FormBody.Builder() .add("username","ibiubiubiu") .add("password","Lhh823924.") .build(); Request request=new Request.Builder() //模擬登入 .url("https://wanandroid.com/user/lg") .post(formBody) .build(); //準備好請求的Call物件 Call call = okHttpClient.newCall(request); try { Response response = call.execute(); Log.i("login",response.body().string()); } catch (IOException e) { e.printStackTrace(); } //請求收藏頁面,必須登入之後才能存取到 request=new Request.Builder() .url("https://wanandroid.com/lg/collect") .build(); //準備好請求的Call物件 call = okHttpClient.newCall(request); try { Response response = call.execute(); Log.i("collect",response.body().string()); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
二.Retrofit的介紹和基本使用
Retrofit是一個基於OkHttp的強大且易於使用的網路請求庫,用於在Android和Java應用程式中進行網路通訊。它有以下的優點:
1.簡化的API: Retrofit提供了一個簡潔、直觀的API,使得定義和執行網路請求變得非常容易。您可以使用註解來描述請求方法、URL路徑、請求引數以及響應型別等資訊,從而減少了樣板程式碼的編寫。
2.攔截器支援: Retrofit完全相容OkHttp攔截器,這使得您可以使用OkHttp的攔截器來自定義和修改請求和響應。這為您提供了更大的靈活性和客製化能力。
/** * 伺服器域名:https://www.httpbin.org/ * 介面:post,引數username,password * 介面:get,引數username,password */ //第一步,根據http介面建立java介面 public interface HttpbinService { @GET("get") //這是GET請求的相對路徑。它指定了在基本URL之後所附加的路徑,以構建完整的請求URL。例如,如果基本URL為https://api.example.com/
,那麼最終的請求URL將是https://api.example.com/get
Call<ResponseBody> get(@Query("username") String username, @Query("password") String password);//注意get請求用@Query註解標註請求引數 @POST("post") @FormUrlEncoded Call<ResponseBody> post(@Field("username") String username,@Field("password") String password);//post請求用@Field註解 @GET Call<ResponseBody> download(@Url String url);//使用Url註解需要提供完整的資源路徑,這時設定的baseUrl就不起作用了 @POST("post") @Multipart Call<ResponseBody> upload(@Part MultipartBody.Part file); }
3.建立Retrofit範例:使用Builder模式建立Retrofit範例,並設定基本的URL以及其他可選的設定,如轉換器、攔截器等。
private Retrofit retrofit; private HttpbinService httpbinService; retrofit=new Retrofit.Builder() .baseUrl("https://httpbin.org/") .build(); httpbinService=retrofit.create(HttpbinService.class);
4.建立API實現:通過Retrofit建立介面的實現,並使用它來執行網路請求。
public void post(){ Call<ResponseBody> call = httpbinService.post("jack", "123456"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { try { Log.i("post",response.body().string()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { } }); } public void get(){ new Thread(new Runnable() { @Override public void run() { Call<ResponseBody> call = httpbinService.get("jack", "password"); try { Response<ResponseBody> response = call.execute(); Log.i("get",response.body().string()); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
檔案的上傳和下載:
public void download(){ Call<ResponseBody> call = httpbinService.download("https://nginx.org/download/nginx-1.20.2.tar.gz"); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if(response.isSuccessful()){ try { InputStream inputStream = response.body().byteStream();
//context.getExternalFilesDir(null)
是一個用於獲取本應用程式的外部儲存目錄的方法,需要注意的是從Android11開始,應用程式不能直接存取SD卡的根目錄,Android應用程式只能在應用的私有目錄或特定的公共目錄中儲存檔案 FileOutputStream out=new FileOutputStream(context.getExternalFilesDir(null).getAbsolutePath()+"/nginx.tar.gz"); byte[] data=new byte[4096]; int len=0; while((len=inputStream.read(data))!=-1){ out.write(data,0,len); } out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { } }); } public void upload(){ MultipartBody.Part video = MultipartBody.Part.createFormData("video", "video.mp4", RequestBody.create(MediaType.parse("video/mpeg4"), new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/video.mp4"))); Call<ResponseBody> call = httpbinService.upload(video); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { try { Log.i("video",response.body().string()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { } }); }
轉換器的使用:在以上的例子中,伺服器返回給我們的結果要麼是字串形式,要麼是輸入流的形式;那如果伺服器給我們返回Json格式的資料,並且我們要求程式將Json自動轉換成對應的javaBean呢,那麼這時就可以用到轉換器了。
比如,伺服器給我們返回的Json字串如下:
{ "code": 0, "msg": "ok", "message": "ok", "data": [] }
那麼,首先我們編寫對應的javaBean,可以自己手寫,也可以找網上的一些轉換工具。
public class Bean{ private Integer code; private String msg; private String message; private List<String> data; public Integer getCode() { return this.code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return this.msg; } public void setMsg(String msg) { this.msg = msg; } public String getMessage() { return this.message; } public void setMessage(String message) { this.message = message; } public List<String> getData() { return this.data; } public void setData(List<String> data) { this.data = data; } @Override public String toString() { return "Bean{" + "code=" + code + ", msg='" + msg + '\'' + ", message='" + message + '\'' + ", data=" + data + '}'; } }
建立Api介面:
public interface Bilibili { @GET("api/tooltip/query.list.do") Call<Bean> get(); //將Call當中的泛型型別改為想要返回的javaBean型別 }
進行網路請求並得到響應結果:
public void converter(){ Retrofit retrofit=new Retrofit.Builder() .baseUrl("https://message.bilibili.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); Bilibili bilibili = retrofit.create(Bilibili.class); Call<Bean> call = bilibili.get(); call.enqueue(new Callback<Bean>() { @Override public void onResponse(Call<Bean> call, Response<Bean> response) { Bean bean = response.body();//響應結果自動轉成了javaBean型別 Log.i("bean",bean.toString()); } @Override public void onFailure(Call<Bean> call, Throwable t) { } }); }
以上就是OkHttp和Retrofit的基本用法了。