深度詳解Retrofit2使用(一)基礎入門

前言

何爲Retrofit?

借用官網的原話,

Type-safe HTTP client for Android and Java by Square, Inc.

適用於Android 和 Java 的類型安全的HTTP客戶端,由Square提供的。(敲黑板)

由此我們可以得知,Retrofit是一種HTTP客戶端框架,使用它,我們可以完成有關HTTP的工作!

(心中是不是會想到,Okhttp也是網絡框架,到底哪一個好用而不貴呢?帶着這個疑問,開始本篇文章!)

目前Retrofit最新版本是2.3.0,可以看看Retrofit的官網Retrofit的github!本文中使用的Retrofit是Retrofit2.3.0版本。

一. Retrofit 入門

1.1 使用Retrofit前 ,需要先引入Retrofit 庫。

  1. 在Android中使用Retrofit ,只需要在build.gradle文件中添加以下代碼,

compile 'com.squareup.retrofit2:retrofit:2.3.0'

然後,同步就可以了!就是這麼簡單!微笑

  2. 在Java中使用Retrofit ,如果沒有使用Maven或者其他項目管理工具的話,那麼就需要我們手動將jar 包下載下來,而且需要下載好幾個jar。下載完成後,加入到項目的build path。具體的需要下載的jar截圖如下所示,


如果使用了Maven或者其他項目管理工具,那麼就只需要在相關的依賴管理文件中加入Retrofit 的依賴即可!例如在Maven依賴管理文件中添加如下代碼,

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>retrofit</artifactId>
  <version>2.3.0</version>
</dependency>
    a. Retrofit 的網絡請求其實是交給okhttp處理的,所以需要引入okhttp,我們都知道okhttp也是Square的開源傑作。(敲黑板)

    b. okio是什麼玩意?okio 是一個包裝了 java.io 和 java.nio api 的庫,以便可以更容易的訪問、存儲以及處理數據,它是Square公司推出的Java IO庫,也是OKHttp依賴的IO庫。(敲黑板)

Retrofit ,OKhttp,Okio 應該可以稱爲Square全家桶吧!偷笑

  PS:

 因爲需要服務端提供接口數據,所以我在自己電腦上搭建了一個web服務器。具體如何搭建,請看java web開發(二) 接口開發

1.2 下面就先以一個實例開始Retrofit !(本文是在Java使用Retrofit ,下篇文章講解在Android使用Retrofit !)

  1. 定義接口。

  使用Retrofit ,首先需要將你的HTTP API改造成Java接口。例如,

	public interface ApiService {

		@GET("StudentInq")
		Call<ResponseBody> getStudents();
	}
ApiService 接口定義了一個方法getStudents(),@GET表示該方法是GET請求,該方法沒有參數,@GET("StudentInq")中的“StudentInq”是path(相對URL),這個path和baseUrl一起組成了接口的請求全路徑,例如baseUrl是“http://localhost:8080/mServer/”,那麼全路徑就是“http://localhost:8080/mServer/getStudent”。(baseUrl下文會提到)

  2. 實例化Retrofit。 

     a. 首先定義了服務請求的URL,

// 服務請求url
	public static final String API_URL = "http://localhost:8080/mServer/";

這個API_URL就是baseUrl,是由ip和端口等組成。

PS: 請求URL,需要以“/”結尾,否則會報錯。(敲黑板)

    b. 創建Retrofit 實例,

	Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();
通過構造者模式創建了Retrofit ,其中設置了請求的

baseUrl。

     c. 接着創建接口實例,

ApiService service = retrofit.create(ApiService.class);
從源碼中可以得知,內部使用 了動態代理模式。

    d. 下面就可以調用接口中的方法了,

// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents();
		//異步執行請求
		call.enqueue(...);
如果是同步請求,調用execute;而發起一個異步請求則調用enqueue。
  下面是完整的代碼,

public class GetTest {

	// 服務請求url
	public static final String API_URL = "http://localhost:8080/mServer/";

	public interface ApiService {

		@GET("StudentInq")
		Call<ResponseBody> getStudents();
	}

	public static void main(String[] args) {
		getList();
	}

	/**
	 * 獲取數據
	 */
	private static void getList() {
		// 創建Retrofit實例
		Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();
		// 生成ApiService接口代理
		ApiService service = retrofit.create(ApiService.class);
		// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents();
		//異步執行請求
		call.enqueue(new Callback<ResponseBody>() {

			@Override
			public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
				// TODO Auto-generated method stub
				try {
					System.out.println(response.body().string());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			@Override
			public void onFailure(Call<ResponseBody> arg0, Throwable arg1) {
				// TODO Auto-generated method stub

			}
		});
	}

}
  運行後的截圖如下,


  使用Retrofit 完成網絡請求確實很方便、快捷,並且代碼量也少!

  上面是使用Retrofit 的簡單實例,可以看到,在定義接口時使用了註解,那麼下面我們就來看看Retrofit 的註解。

二. Retrofit 註解

   Retrofit 共22個註解,這些註解大致分三類,請求方法類、標記類和參數類。Retrofit 的註解是運行時註解,在運行時創建動態代理的方式來提供AOP能力。

  2.1 請求方法類


  1. 序號1~7。

    a. 都是HTTP的請求方法;

    b. 這些註解都有一個value()方法,例如,

		@GET("StudentInq")
		Call<ResponseBody> getStudents();

    下面是GET註解的源碼,

它有一個value()方法,接收一個字符串(StudentInq),“StudentInq”這個就是path,也就是相對URL,它與baseUrl組成全路徑;

    c. 這個path可以使用變量,如 {id} ,並使用 @Path("id") 註解爲 {id} 提供值,例如;

@GET("StudentInq/{id}")
		Call<ResponseBody> getStudentById(@Path("id")int id);
  2. 序號8,HTTP註解。

    a. 用於替代以上7個註解,以及其他擴展方法;

    b. 有三個屬性,method、path、hasBody,首先看看HTTP註解的源碼,


     method:請求方法(區分大小寫),

     path:請求相對路徑,

     hasBody:是否有body(請求體),

下面是HTTP註解的使用例子,

	@HTTP(method="GET",path="StudentInq/{id}",hasBody=false)
		Call<ResponseBody> getStudentById(@Path("id")int id);
  2.2 標記類

  2.3 參數類


    1. @Headers

    使用 @Headers 註解設置固定的請求頭,所有請求頭不會相互覆蓋,即使名字相同。

    2. @Header

    使用 @Header 註解動態更新請求頭,匹配的參數必須提供給 @Header ,若參數值爲 null ,這個頭會被省略,否則,會使用參數值的 toString 方法的返回值。

    3. @Body

    使用 @Body 註解,指定一個對象作爲 request body。

    4. @Field

    表單提交,與 FieldMap、FormUrlEncoded註解 配合使用。

    5. @FieldMap

    表單提交,與 Field、FormUrlEncoded 註解配合使用;接受 Map<String, String> 類型,非 String 類型會調用 toString() 方法。

    6. @Part

    表單字段,與 PartMap、@Multipart註解 配合,適合文件上傳情況。表示多部分請求中的單個部分。該註解的的參數類型有三種:

       a. 如果類型是MultipartBody.Part,則內容將直接使用。 忽略註釋中的名稱(即@Part MultipartBody.Part部分);

       b. 如果類型是RequestBody,則該值將直接與其內容類型一起使用。 在註釋中提供零件名稱(例如@Part(“foo”)RequestBody foo);

       c. 其他對象類型將通過使用轉換器轉換爲適當的表示形式。 在註釋中提供零件名稱(例如,@Part(“foo”)Image photo);值可以是null,它將從請求主體中被省略。

    7. @PartMap

    表單字段,與 Part 、@Multipart註解配合,適合文件上傳情況;默認接受 Map<String, RequestBody> 類型,非 RequestBody 會通過 Converter 轉換。

    8. @Path

    請求 URL 可以替換模塊來動態改變,替換模塊是 {}包含的字母數字字符串,替換的參數必須使用 @Path 註解的相同字符串。

    9. @Query和@QueryMap

    將數據轉換成“鍵=值”的形式,並且添加到URL的結尾。例如,id=110&sex=nan。

    10. @Url

    動態指定Path路徑。

  PS:

    1. Query、QueryMap 與 Field、FieldMap 功能一樣,生成的數據形式一樣;只不過Query、QueryMap 的數據體現在 Url 上;Field、FieldMap 的數據是請求體;

    2. {佔位符}和PATH儘量只用在URL的path部分,url中的參數使用Query和QueryMap 代替,保證接口定義的簡潔。

  以上就是Retrofit 的全部註解。這些概念還是比較抽象,下面就通過實例,看看這些註解的具體使用。

三. 註解實例

  3.1 @Path 使用。

使用GET請求,接口請求全路徑是“http://localhost:8080/mServer/getStudent/1”,下面是具體實現代碼,

public class PathTest {

	// 服務請求url
	public static final String API_URL = "http://localhost:8080/mServer/";

	public interface ApiService {

		@GET("getStudent/{id}")//這裏的{id} 表示是一個變量
		Call<ResponseBody> getStudents(@Path("id") int id);

	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		getList();
	}

	/**
	 * 獲取數據
	 */
	private static void getList() {
		// 創建Retrofit實例
		Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();
		// 生成ApiService接口代理
		ApiService service = retrofit.create(ApiService.class);
		// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents(1);
		// 異步執行請求
		call.enqueue(new Callback<ResponseBody>() {

			@Override
			public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
				// TODO Auto-generated method stub
				try {
					System.out.println(response.body().string());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			@Override
			public void onFailure(Call<ResponseBody> arg0, Throwable arg1) {
				// TODO Auto-generated method stub

			}
		});
	}

}
運行後客戶端的截圖,

並且我在服務端打印了RequestURI和RequestURL,下面是運行截圖,


  3.2 @Query、@QueryMap和@URL 使用。

    1. 首先看@Query註解。定義接口,

...
@GET("StudentInq")
		Call<ResponseBody> getStudents(@Query("sex") String sex,@Query("mobile") String mobile);
...

接口調用,

...
// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents("nan","123456789");
...
我在服務端doget()方法中打印了請求參數和其他信息,下面是主要代碼,
...
Enumeration enu=request.getParameterNames();  
		while(enu.hasMoreElements()){  
		String paraName=(String)enu.nextElement();  
		System.out.println(paraName+": "+request.getParameter(paraName));  
		}
		
		System.out.println("getRequestURI: "+request.getRequestURI());  
		System.out.println("getRequestURL: "+request.getRequestURL());  
		System.out.println("getQueryString: "+request.getQueryString());  //獲取get方式中的參數列表
...
運行後服務端截圖,


  2. 接着是@QueryMap註解。

定義接口,

...
	@GET("StudentInq")
		Call<ResponseBody> getStudents(@QueryMap Map<String, String> map);
...
接口調用,
...
Map<String, String> map=new HashMap<>();
		map.put("id", "110");
		map.put("sex", "nan");
		// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents(map);
...
運行後服務端截圖,

  小結:

    @Query註解和@QueryMap註解,他們的功能本質上是一樣的,都是把數據轉化成類似“id=110&sex=nan”這樣的格式,並且添加到請求的路徑尾部;只不過@Query是一個一個接收參數,而@QueryMap是一次性接收一個集合。

    @Query註解和@QueryMap註解的參數如果不是String(或Map的第二個泛型參數不是String)時會被默認會調用toString轉換成String類型。

  3. @URL註解。

定義接口,

...
		@GET
		Call<ResponseBody> getStudents(@Url String name);
...
接口調用,
...
		// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents("StudentInq");
...
就不顯示運行後服務端截圖了,前面的運行結果一樣!使用@URL註解可以設置Path(相對URL)。
  3.3 @Headers和@Header註解。

首先是@Headers註解。

...		
@Headers({"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
		    "User-Agent: Retrofit-Sample-App",
             "Cache-Control:no-cache"})
		@GET("StudentInq")
		Call<ResponseBody> getStudents();
...
定義接口後,調用接口。我在服務端打印了請求頭信息,具體代碼如下,
...
		Enumeration<String> reqHeadInfos = request.getHeaderNames();// 獲取所有的請求頭
		while (reqHeadInfos.hasMoreElements()) {
			String headName = (String) reqHeadInfos.nextElement();
			String headValue = request.getHeader(headName);// 根據請求頭的名字獲取對應的請求頭的值
			System.out.println(headName + ": " + headValue);
		}
...
調用接口後服務端截圖,


  接着看@Header註解,

定義接口,

...
		@GET("StudentInq")
		Call<ResponseBody> getStudents(@Header("Pragma") String Pragma);
...
調用接口,
...
// 調用具體接口方法
		Call<ResponseBody> call = service.getStudents("no-cache");
...
調用接口後服務端截圖,


  小結:@Headers和@Header這兩個註解都是用來設置請求頭信息的,只不過@Headers是設置固定值的請求頭,而@Header可以設置動態的請求頭。

  3.4 @Field和@FieldMap。

這兩個註解,我們前面已經給出了定義,需要配合@FormUrlEncoded使用。

定義接口,

...
		
		@FormUrlEncoded
		@POST("getStudent")
		Call<ResponseBody> getStudent(@Field("id")int id,@Field("name")String name,@Field("sex")String sex);
		
		@FormUrlEncoded
		@POST("getStudent")
		Call<ResponseBody> getStudent(@FieldMap Map<String, String>map);
...
定義了兩個方法,都是POST請求。第一個方法使用@Field定義了幾個參數,而第二個方法使用@FieldMap定義了一個map。接口調用,
...
// 調用具體接口方法
				Call<ResponseBody> call = service.getStudent(111,"xx","nan");
		Map<String, String> map=new HashMap<>();
		map.put("id", "119");
		map.put("name", "xw");
		map.put("sex", "nan");
		// 調用具體接口方法
		Call<ResponseBody> call1 = service.getStudent(map);
...

我在服務端doPost()方法中打印請求參數列表,具體代碼如下,

...
		Enumeration enu=request.getParameterNames();  
		while(enu.hasMoreElements()){  
		String paraName=(String)enu.nextElement();  
		System.out.println(paraName+": "+request.getParameter(paraName));  
		}
...
調用接口後服務端截圖,


  小結:@Field和@FieldMap這兩個註解,在本質上是一樣的,都是將請求的數據按照 key1=val1&key2=val2 的方式進行編碼,並放置在請求體中,並且Form表單的編碼格式必須是“application/x-www-form-urlencoded”(也就是POST需要使用@FormUrlEncoded)。換句話說,在使用@Field和@FieldMap這兩個註解時,需要滿足兩個方面:

    1. 接口方法必須是POST請求;

    2. 接口方法必須有@FormUrlEncoded註解;

如果在使用時,缺少上面的某一條件,程序會報錯!至於二者的區別也很明顯,就不多說了!

  至此,有關注解方面的,就到此結束了!


  由於篇幅原因,有關Retrofit的其他內容,就暫時放到下篇文章!詳情請看 深度詳解Retrofit2使用(二)實踐














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章