Retrofit
Retrofit 是一套由 Square 所開發維護,將 RESTful API 寫法規範和模組化的函式庫。 底層也使用他們的 okHttp ,okHttp 用法參考 okHttp 章節。
Retrofit 預設回傳的處理器是現代化 API 最流行的 JSON,如果你要處理別的要另外實作 Converter。
如果需要實作 Server 驗證,建議做好另外接上 okHttpClient 去設 Interceptor。在 Retrofit 1.9.0 的 Interceptor 中能做的有限。
限制
- Java 6(含) 和 Android 2.3(含) 以上
網路資源
Name | Link |
---|---|
github | https://github.com/square/retrofit |
官網 | http://square.github.io/retrofit/ |
安裝方式及 ProGuard 設定
請以 官網 為準
先備知識
繼續閱讀前以下東西要先知道。
- RESTful API:不知道的強烈建議先去了解一下。
- JSON:就格式要知道。
- Gson:稍微要了解一點用法。
- HTTP 的 GET、POST、PUT、DELETE 了解一下。
如何使用
這邊以寫作時最新的 Retrofit 1.9.0 版為準
常見用法
直接跳級來個難一點的吧~
建立你的 Server 的設定檔,通常都會有正式環境和測試環境和 API 版本。
public class Config {
public enum Env {
PROD("example.com"),
DEV("dev.example.com");
public final String host;
Env(String host) {
this.host = host;
}
}
// API 要使用的 API 版本
public final static int VERSION = 1;
// 要去連的環境,這邊設定為正式環境
public final static Env env = Env.PROD;
public static String getEndPoint() {
return getEndPoint(Config.env, VERSION);
}
public static String getEndPoint(Env env, int version) {
return String.format("https://%s/api/v%d", env.host, version);
}
}
以下用大家已經做到爛的論壇的文章和文章評論為例。 定義好和回傳 JSON 格式相符的 Geon Data Object
請無視重複的 article_id。 實際使用時,請做好封裝或是用 lombok。
文章 Response 的 JSON 及對應的 class
{
"id": 1,
"user_id": 1,
"content": "有地震",
"created_at": "2015-03-11T20:36:42.000Z",
"updated_at": "2015-04-27T09:06:56.000Z",
"comments": [
{
"id": 1,
"article_id": 1,
"user_id": 2,
"content": "頭推",
"created_at": "2015-03-11T20:46:42.000Z",
"updated_at": "2015-03-11T20:46:42.000Z"
},
{
"id": 1,
"article_id": 1,
"user_id": 5,
"content": "好快,輸了。",
"created_at": "2015-03-11T20:57:31.000Z",
"updated_at": "2015-03-11T20:57:31.000Z"
},
]
}
public class Article {
int id;
@SerializedName("user_id")
int userId;
String content;
@SerializedName("created_at")
String createdAt;
@SerializedName("updated_at")
String updatedAt;
@SerializedName("comments")
List<Comment> comments = new ArrayList<Comment>();
}
文章評論 Response 的 JSON 及對應的 class
{
"id": 1,
"article_id": 1,
"user_id": 2,
"content": "頭推",
"created_at": "2015-03-11T20:46:42.000Z",
"updated_at": "2015-03-11T20:46:42.000Z"
}
public class Comment {
int id;
@SerializedName("article_id")
int articleId;
@SerializedName("user_id")
int userId;
String content;
@SerializedName("created_at")
String createdAt;
@SerializedName("updated_at")
String updatedAt;
}
定義與 API 符合的 Interface,這是 Retrofit 的最大賣點。
public interface ArticlesApi {
String CONTENT = "content";
//文章列表
@GET("/articles")
List<Article> index();
//取得某篇文章
@GET("/articles/{id}")
Article show(
@Path("id") int id
);
//新增文章
@POST("/articles")
@FormUrlEncoded
Article create(
@Field(CONTENT) String content
);
//編輯文章
@PUT("/articles/{id}")
@FormUrlEncoded
Article update(
@Path("id") int id,
@Field(CONTENT) String content
);
//編輯文章
@PUT("/articles/{id}")
Article update(
@Path("id") int id,
@Body Article article
);
//刪除文章
@DELETE("/articles/{id}")
Response delete(@Path("id") int id);
}
public interface CommentsApi {
String CONTENT = "content";
//取得某篇文章的評論
@GET("/articles/{article_id}/comments")
@FormUrlEncoded
List<Comment> index(
@Path("article_id") int articleId,
@Field(CONTENT) String comment
);
//新增評論
@POST("/articles/{article_id}/comments")
@FormUrlEncoded
Comment create(
@Path("article_id") int articleId,
@Field(CONTENT) String content
);
//新增評論
@POST("/articles/{article_id}/comments")
Comment create(
@Path("article_id") int articleId,
@Body Comment comment
);
//編輯評論
@PUT("/comments/{id}")
@FormUrlEncoded
Comment update(
@Path("id") int id,
@Field(CONTENT) String content
);
//編輯評論
@PUT("/comments/{id}")
Comment update(
@Path("id") int id,
@Body Comment comment
);
//刪除評論
@DELETE("/comments/{id}")
Response delete(@Path("id") int id);
}
建立負責處理 API 的 class
public class ApiClient {
private final RestAdapter restAdapter;
private final ArticlesApi articlesApi;
private final CommentsApi commentsApi;
public ApiClient() {
//這邊的使用情境為全部 API 都對同一個 Server 操作
restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.getEndPoint())
.build();
articlesApi = restAdapter.create(ArticlesApi.class);
commentsApi = restAdapter.create(CommentsApi.class);
}
/*
* 簡單示範幾個方法,實際使用可在這裡作內容驗證
*/
public Article showArticle(int id) {
return articlesApi.show(id);
}
public Comment createComment(int articleId, String content) {
return commentsApi.create(articleId, content);
}
//只能透過傳入 Comment 物件刪除,避免誤傳如文章 id 之類的
public boolean deleteComment(Comment comment) {
return deleteComment(comment.id);
}
//將根據 id 刪除評論的方法封裝起來
private boolean deleteComment(int id) {
Response response = commentsApi.delete(id);
final int statusCode = response.getStatus();
return 200 <= statusCode && statusCode < 300;
}
}
Sign Request
如果 API 需要做 sigunature,在 Retrofit 的 Interceptor 無法做到,要抽換掉Client 改用 okHttp 的 Interceptor 才可以。 用法參考 okHttp#Interceptor
public class ApiClient {
private final RestAdapter restAdapter;
private final OkHttpClient client;
private final Interceptor signRequestInterceptor = new SignRequestInterceptor();
public ApiClient() {
client = new OkHttpClient();
client.networkInterceptors().add(signRequestInterceptor);
OkClient retrofitClient = new OkClient(client);
restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.getEndPoint())
.setClient(retrofitClient)
.build();
}
...
}
Handle Structured JSON
API 回傳具有格式的 JSON 時,請參考 GSON 部分的 Handle Structured JSON
將設定好的 Gson 放到 GsonConverter 內即可。用法如下:
public class ApiClient {
private final RestAdapter restAdapter;
public ApiClient() {
restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.getEndPoint())
.setConverter(new GsonConverter(new GsonBuilder()
.registerAdapterFactory(new ItemAdapterFactory())
.setDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" )
.create();
))
.build();
}
}
Share GSON Model With Realm Database Model
如果你的 Database 剛好使用 Realm 並且想共用你的 GSON API Model
詳細請見 GSON 的 Share GSON Model With Realm Database Model
public class ApiClient {
private final RestAdapter restAdapter;
public ApiClient() {
restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.getEndPoint())
.setConverter(new GsonConverter(new GsonBuilder()
.setExclusionStrategies(new RealmExclusionStrategy())
.create();
))
.build();
}
}
附錄
Cheat Sheet
最常見的 4 種 HTTP Request (GET、POST、PUT、DELETE) 及相關變化在 Retrofit 內的寫法。
GET
@GET("/articles")
List<Article> index();
Request 路徑由參數組成,使用中括號{``}
和@Path
@GET("/users/{id}")
User show(
@Path("id") int id
);
GET 加上 query string,使用@Query
@GET("/search")
List<Place> search(
@Query("query") String query,
@Query("north") Float boundNorth,
@Query("south") Float boundSouth,
@Query("west") Float boundWest,
@Query("east") Float boundEast,
@Query("order") String sortKey,
@Query("asc") String ascOrDesc
);
POST or PUT 靠 GSON 幫忙 Serailize 來送出 JSON 資料,POST 和 PUT 使用@Body
指定 POST Body
@POST("/articles/{article_id}/comments")
Comment create(
@Path("article_id") int articleId,
@Body Comment comment
);
POST or PUT 自己組的 JSON String
@POST("/articles/{article_id}/comments")
Comment create(
@Path("article_id") int articleId,
@Body TypedInput comment
);
String json = "{\"rating\":3,\"content\":\"my content...\"}";
TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
POST or PUT Url encode 過的表單資料,用@FormUrlEncoded
,使用@Field
個別指定
@POST("/articles/{article_id}/comments")
@FormUrlEncoded
Comment create(
@Path("article_id") int articleId,
@Field("rating") int rating,
@Field("content") String content
);
POST or PUT Url encode 過的表單資料,用@FormUrlEncoded
,參數也可用@Body
傳
@POST("/articles/{article_id}/comments")
@FormUrlEncoded
Comment create(
@Path("article_id") int articleId,
@Body Comment comment
);
POST or PUT 用@Multipart
來上傳檔案
@PUT("/me")
@Multipart
Me update(
@Part("display_name") String displayName,
@Part("avatar") TypedFile avatar
);
File file = ...
TypedFile avatar = new TypedFile("multipart/form-data", file);
DELETE
@DELETE("/comments/{id}")
Response delete(
@Path("id") int id
)