前面我們學習過了Android網絡開發中的Okhttp架構和Asynchttpclient架構,這一篇我們學習一個非常強大的架構——Retrofit架構。Retrofit現在最新版本是2.1.0,Retrofit架構是Square公司推出來的,是在Okhttp基礎上的進一步封裝。
在這裡對Okhttp做一些說明:Okhttp是基于HTTP協定封裝的,跟HttpClient、HttpUrlConnection的職責是一樣的,雖然Okhttp也可以開線程,但它是更多偏向于真正的網絡通信。Okhttp是基于NIO和OKio的,這裡可能很多人不明白什麼是IO、NIO和Okio,這裡做一個簡單的解釋:IO和NIO是Java的概念,IO是一種阻塞式的資料讀取,舉個例子:我向網絡中請求資料,程式就一種在等待,等到網絡中傳回資料并解析好了,程式才往下執行,這種就是阻塞式的;而NIO則是非阻塞式的,還是剛才那個例子,向網絡請求資料,但是程式繼續執行,等到資料傳回和處理完才通知我,然後通過回調處理,這種就是非阻塞式的,非阻塞式的效率比阻塞式的高。而Okio是Square公司在IO和NIO基礎上封裝的一個更高效、更簡單的資料處理庫。
下面我們介紹一下Retrofit這個架構
概述
Retrofit架構是基于Okhttp封裝的,在1.0版本的時候是預設采用Okhttp通信,不過在沒有添加Okhttp依賴的時候也可以采用自帶的網絡請求通信,解析資料方式是Gson(一個Google推出的Json資料解析架構,後面我們會介紹),是以我們需要添加Gson的依賴。但是在2.0後的版本中,Retrofit是必須要采用Okhttp請求,還有請求傳回資料的解析也不是Gson,需要我們自己手動指定。
Retrofit是通過注解将HTTP請求轉化為Java接口,那麼,我們了解一下Retrofit提供有哪些注解?
請求方式的注解
- DELETE:建構一個DELETE請求
- GET:建構一個GET請求
- HTTP:可以代替其它的任意一種
- OPTIONS:建構一個OPTIONS請求
- PATCH:建構一個PATCH請求
- POST:建構一個POST請求
- PUT:建構一個PUT請求
上面列出的都是Retrofit2.0提供的網絡請求方式的注解,其中,我們可以通過HTTP這個注解自定義一個請求方式,例如:
interface Service {
@HTTP(method = "CUSTOM", path = "custom/endpoint/")
Call<ResponseBody> customEndpoint();
}
甚至HTTP注解也可以用于發送一個DELETE請求方式的請求體,例如:
interface Service {
@HTTP(method = "DELETE", path = "remove/", hasBody = true)
Call<ResponseBody> deleteObject(@Body RequestBody object);
}
标記注解
- FormUrlEncoded注解:與@Field或者@FieldMap一起使用,上傳表單資料
- Multipart注解:與@Part一起使用
- Streaming注解:用于下載下傳大檔案
參數注解
- Body注解::用于POST請求體,将執行個體對象根據轉換方式轉換為對應的json字元串參數,這個轉化方式是GsonConverterFactory定義的。
- Field注解:@Field和@FormURLEncoded一起使用,表示送出一個表單資料給伺服器,我們都知道,表單資料是以鍵值對的形式存在,是以,@Field注解中的參數字元串為“鍵”,而被@Field标注的參數值為 “值”,例如:
@FormUrlEncoded @POST("/list") Call<ResponseBody> example(@Field("name") String... names);
- FieldMap注解:這個注解跟上面的@Field注解差不多,也是和@FieldURLEncoded一起使用。注解的是一個Map,鍵作為表單資料的”鍵”,值作為表單資料的”值”,例如
@FormUrlEncoded @POST("/things") Call<ResponseBody> things(@FieldMap Map<String, String> fields);
- Part注解:與@PartMap一樣
- PartMap注解:用于POST檔案上傳,其中@Part MultipartBody.Part代表檔案,@Part(“key”)RequestBody代表參數,需要添加@Multipart表示支援檔案上傳的表單,Content-Type: multipart/form-data,例如:
@Multipart @POST("/upload") Call<ResponseBody> upload( @Part("file") RequestBody file, @PartMap Map<String, RequestBody> params);
- Query注解:表示查詢參數,非常簡單
- QueryMap注解:查詢參數,用于GET查詢,需要注意的是@QueryMap可以約定是否需要encode
其它注解
- Path注解:URL占位符,我們看一個例子:
@GET("/image/{id}") Call<ResponseBody> example(@Path("id") int id); @GET("/user/{name}") Call<ResponseBody> encoded(@Path("name") String name); 就是将例子中用@Path标注的參數用來替換伺服器位址中使用“{}”括起來的字元串,簡單說就是,這是一個占位符。 但是注意的是,不能替換URL中的字元串,例如:在http://www.hitomis.com/user.do?action=findUser&username="zuck"中,不能替換action=findUser&username="zuck"路徑中的内容
- Url注解:使用全路徑複寫baseUrl,用于不是同一baseUrl的場景
- Header注解:設定請求頭,可以不設定。設定的話就是@Header裡面注解參數為”鍵”,被@Header注解的參數作為”值”,例如:
@GET("/") Call<ResponseBody> foo(@Header("Accept-Language") String lang);
- Headers注解:用于修飾方法,用于設定多個Header值
- HeaderMap注解:與Header類似
好了,Retrofit就簡單介紹到這裡,下面我們還是學習一下怎麼使用這個架構!
使用
Android Studio使用需要在Gradle中添加依賴,建議先去GitHub上擷取最新的依賴,在這裡給出Retrofit的GitHub位址和官網位址:
GItHub位址:https://github.com/square/retrofit
官網位址:http://square.github.io/retrofit/
compile 'com.squareup.retrofit2:retrofit:2.1.0'
這是Gradle依賴,我們需要用到java/Json轉換器工廠,是以需要添加轉換器的依賴,可以直接在AS裡面搜尋最新,包括上面的Retrofit依賴也可以在AS裡面搜尋
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
如果你想用RxJava支援的擴充卡的話,需要添加擴充卡的依賴,也可以直接在AS裡面搜尋,這裡我們沒有用到,是以就不需要添加支援RxJava擴充卡的依賴。
簡單描述一下使用Retrofit架構的基本步驟:
- 第一步當然就是添加依賴了,Android開發的話基本都是用Gradle的,如果是Java可以用Maven。
- 第二步則是定義一些接口,Retrofit是面向接口程式設計的。
- 第三步是建立Retrofit對象,一般是使用Builder模式建立這個對象
- 第四步就是使用Retrofit。
上面列出的就是使用Retrofit的基本步驟,下面我們具體使用一下這個強大的網絡請求架構
我們說過Retrofit是面向接口程式設計的,是以使用之前我們需要定義一些我們用到的接口,那麼我們先看一下我們定義的接口:
package com.example.frame.common;
import com.example.frame.bean.ContributorsBean;
import java.util.List;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
import retrofit2.http.Url;
/**
* Created by :Huawen
* Created time : 2016/9/22 16:15
* Description :
* Github: https://github.com/Devin1102
*/
public interface RetrofitAPI {
/**
* 普通的請求
*
* @return
*/
@GET("repos/square/retrofit/contributors")
Call<List<ContributorsBean>> getData();
/**
* 查詢
*
* @param username
* @return
*/
@GET("sayHello")
Call<ResponseBody> getQuery(@Query("username") String username);
/**
* 上傳檔案
*
* @param fileUrl
* @return
*/
@GET
Call<ResponseBody> downloadFiel(@Url String fileUrl);
/**
* 上傳檔案
*
* @param description
* @param file
* @return
*/
@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(@Part("description") ResponseBody description, @Part MultipartBody.Part file);
/**
* 上傳表單資料
*
* @param username
* @param password
* @return
*/
@FormUrlEncoded
@POST("sayHello")
Call<ResponseBody> postForm(@Field("username") String username, @Field("password") String password);
}
除了接口之外,我們還将Retrofit對象的擷取封裝了,看一下封裝的代碼:
public class RetrofitService {
protected Retrofit getRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UrlUtils.GET_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
public RetrofitAPI createRetrofitAPI() {
return getRetrofit().create(RetrofitAPI.class);
}
}
這裡也沒有什麼好解析,我們這裡沒有添加OkhttpClient端,Retrofit會自動建立一個,當然我們也可以自己手動設定一個。
使用Retrofit實作簡單的GET請求
使用Retrofit架構完成GET請求非常簡單,我們先看一下代碼:
private void retrofitGet() {
Call<List<ContributorsBean>> call = mRetrofit.getData();
call.enqueue(new Callback<List<ContributorsBean>>() {
@Override
public void onResponse(Call<List<ContributorsBean>> call, Response<List<ContributorsBean>> response) {
Log.i(TAG, "onResponse請求成功");
List<ContributorsBean> beans = response.body();
for (ContributorsBean contributorsBen : beans) {
Log.i(TAG, "onResponse" + contributorsBen);
}
}
@Override
public void onFailure(Call<List<ContributorsBean>> call, Throwable t) {
Log.i(TAG, "onFailure請求失敗");
}
});
}
這裡對應的接口裡面的GET請求,裡面我們沒有設定參數。這種請求跟Okhttp架構請求方式差不多,使用的也是異步的請求。
Retrofit實作Query參數請求
這個是一個簡單的GET請求,不過設定一些參數,上面我們說過@Query和@QueryMap是用于GET請求的設定參數,我們看一下具體的代碼:
private void retrofitGetQuery() {
Call<ResponseBody> call = mRetrofit.getQuery("Devin");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, getResources().getString(R.string.req_failed));
}
});
}
這裡我們設定一個參數,與接口一緻。
Retrofit實作POST上傳表單資料
我們在做實際項目的時候,需要向伺服器傳遞一些表單資料,比如登入、注冊功能的時候,Retrofit也給我們提供了實作表單資料上傳的方法,我們看一下具體的例子:
private void retrofitPostForm() {
Call<ResponseBody> call = mRetrofit.postForm("Devin", "Devin");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, getResources().getString(R.string.req_failed));
}
});
}
設定表單資料上傳的時候需要在接口中設定@FormUrlEncoded這個方法标記,通過@Field或者@FieldMap實作設定資料。
Retrofit實作檔案下載下傳
檔案下載下傳是經常使用的網絡操作,比如圖檔和視訊下載下傳、APP更新等。我們看一下Retrofit架構提供的檔案下載下傳功能:
private void retrofitDownloadFile() {
Call<ResponseBody> call = mRetrofit.downloadFiel("http://repo1.maven.org/maven2/com/squareup/retrofit2/retrofit/2.1.0/retrofit-2.1.0.jar");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
InputStream is = response.body().byteStream();
try {
FileOutputStream fos = new FileOutputStream(new File("/sdcard/retrofit-2.1.0.jar"));
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
}
});
}
在定義檔案下載下傳接口的時候,我們設定了@Url參數,這個是複寫BaseUrl的,用于我們不使用baseUrl的情況,裡面被@Url标志的資料是字元串類型,就是檔案下載下傳的URL。然後下載下傳成功之後,以讀流的形式将檔案儲存起來。
Retrofit實作檔案上傳
檔案上傳也是經常使用的功能,我們看一下Retrofit架構實作檔案上傳的功能:
private void retrofitUploadFile() {
String descriptionString = "UploadFile";
ResponseBody description = ResponseBody.create(MediaType.parse("multipart/form-data"), descriptionString);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), new File("/sdcard/retrofit-2.1.0.jar"));
MultipartBody.Part body = MultipartBody.Part.createFormData("picture", "retrofit-2.1.0.jar", requestFile);
Call<ResponseBody> call = mRetrofit.uploadFile(description, body);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.i(TAG, "onFailure" + getResources().getString(R.string.req_failed));
}
});
}
檔案上傳需要在接口中設定@Multipart标記,在接口中我們設定了兩個參數:@Part(“description”) ResponseBody description, @Part MultipartBody.Part file,我們需要建構兩個參數,然後實作上傳。
以上是我對Retrofit架構的基本了解和具體的使用,可能寫法不是很好,如果大家有更好的寫法,歡迎大家留言交流。
上面Demo已經上傳到GitHub了,當然還有其它架構的使用Demo,Demo的GitHub位址:https://github.com/Devin1102/AndroidFrameDemo