亂七八糟的個人感慨(個人淺見,勿噴!!)
不特別願意使用Retrofit框架,每加一個接口都需要到接口裏定義方法,還要添加相關請求實現,每次請求都要做這麼幾步,私下裏也曾想過封裝成泛型,實踐後發現有點問題,註解Method返回的Call這裏會出問題,不能取到明確類型。
這個框架你不能說個人用着感覺不方便就放棄,不去了解,但從團隊考慮隊友都會用,而你不會這就是你的不對了,所以一定要會熟練使用Retrofit+Okhttp框架,至於RX系列目前還沒到那種必須會的程度,自行考量吧。
目前呢很多人說你怎麼還在用xutils 、volley這些過時的框架啊?我只想說:能被很多人使用過的技術,說明他也有閃光點,這點誰都不能抹殺,很多新興的框架出現,比現有的框架還要完美,這並不是說現有框架就是過時了,一個項目用什麼框架取決於項目大小、團隊配合、項目維護成本等因素。最近有些人再吹RX+Dagger2+Retrofit2+Okhttp3+MVP,聽上去是那麼回事,讓人感覺你很nb,但是你在想想,如果我的項目只有三個接口5個簡單的界面,再採用這一套框架,代碼要多些多少呢?開發週期又會增加多少呢?如果你突然辭職了,公司招人不會RX 、Dagger2這些框架咋辦呢?這些都是技術成本,都是你爲公司留下的。作爲程序員僅僅考慮代碼寫得漂亮,有沒有考慮過產出的效率問題呢?有沒有爲公司考慮過呢?
最後在說一下框架過時這個問題,每個流行過的框架,他的代碼設計思想、模式等技術核心都值得我們去學習,如果你想說他過時了,就看不起它了,那麼我想請問你兩三個問題:你很牛逼麼?你是大牛你能寫出這樣的框架麼?請把你寫好的託管在github的框架地址給我一個,可以麼?
Retrofit
配置開發環境
Retrofit requires at minimum Java 7 or Android 2.3.
要求開發環境JDK1.7+ android SDK 2.3+,目前Retrofit出了多個版本,項目導入建議使用release版本,最新版本自行跟蹤項目託管地址
//github地址: https://github.com/square/retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
基本的請求流程
導入項目後,通過ServiceGenerator方法將創建HTTP客戶端包括頭定義的授權,調用自己定義的接口註解的方法獲取返回值。關於這裏要注意區分1.9版本與2.0+是有區別的,個人使用2.0版本這裏就不提1.x版本相關知識,如想了解更多請參考這裏(下面提到的Retrofit都是隻Retrofit2.0別咬文嚼字啊)
// https://futurestud.io/tutorials/android-basic-authentication-with-retrofit
在2.0以後Retrofit不再依賴Gson,使用需要自己添加依賴(當然你也可以使用Jackson)
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
請求流程概要分爲以下幾步(這裏以發送驗證碼爲例)
定義響應實體類ResponseBean.java
定義接口SendAuthCodeService
/**
* Created by idea on 2016/11/18.
*/
public interface SendAuthCodeService {
@POST
Call<ResponseBean> sendAuthCode();
}
- 定義Service
public class ServiceGenerator {
public static final String API_BASE_URL = "";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL).....略..
public static <S> S createService(Class<S> serviceClass, final String phone) {
//................略過攔截器處理..........................
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
}
- 調用(Call的方法有異步和同步,使用時各取所需)
public void sendAuthCode(String phone){
SendAuthCodeService sendAuthCodeService =
ServiceGenerator.createService(SendAuthCodeService.class, "15828331414");
Call<ResponseBean > responseBean = sendAuthCodeService.sendAuthCode();
responseBean.enqueue(new Callback<ResponseBean>(){
@Override
public void onResponse(Call<ResponseBean> call, Response<ResponseBean> response) {
}
@Override
public void onFailure(Call<ResponseBean> call, Throwable t) {
}
});
}
請求攜帶token和令牌驗證
每個app賬戶登錄基本都會有這麼一個token值,作爲訪問憑證,每次訪問都要攜帶,Okhttp有攔截器這麼個功能可以幫我們實現,至於攔截器這塊知識稍後再提。下面是相關實現代碼塊
public static <S> S createService(Class<S> serviceClass, final String authToken) {
if (authToken != null) {
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", authToken)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}
經常我們登錄緩存了token,而每次需要同步用戶信息就可以用到這個玩意兒了,代碼調用示例如下(OAuth 這塊略過,知識點都差不多。)
UserService userService =
ServiceGenerator.create(UserService.class, "auth-token");
Call<User> call = userService.getUserInformation();
User user = call.execute().body();
一個key對應1個或多個value的GET請求查詢
服務器的api無非就是增刪改查,客戶端傳遞參數執行這些操作。傳遞這些參數在Retrofit框架下該怎麼使用呢?且看下面代碼塊
public interface UserService {
@GET("/user")
Call<User> getUserInformation(@Query("userId") long userId);
}
只需要註解Query的字段參數即可,這裏補充說明一點:@GET @POST這些作用在方法之上的註解可以含參,用於拼接完整api接口,當@GET或@POST註解的url爲全路徑時,會直接使用註解的url。(從某個層面來講,對外只有一個接口,具體訪問哪個接口,只需要把這裏GET/POST的Path作爲參數通過post提交可能會更好一點)
baseUrl = "http://www.baidu.com"
@GET("/user")
requestUrl = "http://www.baidu.com/user"
// 補充:@Url作爲參數傳遞,@Url註解的路徑如果爲全路徑,則直接使用它,否則通過BaseUrl+@Url拼接
如遇到如下需求: hr林妹妹要統計公司員工都是211、985工程院校畢業有多少人?這個查詢條件多個值怎麼辦呢?Retrofit對這種需求也是信手拈來
public interface UserService {
@GET("/user")
Call<Integer> getUserCount(@Query("school") List<Integer> schools);
}
schools: [211,985]
拼接後的Url: http://www.baidu.com/user?school=211&school=985
同步異步請求
同步請求
上面有提到過同步和異步請求,這裏簡單來了解相關知識。下面是一個同步請求接口定義實例代碼
public interface UserService {
@GET("/user")
Call<User> getUserInformation(@Query String token);
}
同步請求的數據處理如下
Call<User> call = userService.getUserInformation("Uid1232342");
user = call.execute().body();
Warning: 同步請求可能是導致APP在4.0及以上版本崩潰,你可能會得到一個Exception 的異常錯誤,爲了不阻塞UI,一般通過handler message來解決,這種使用方法很不爽的
異步請求
異步請求通過接口回調方式返回結果,也是我們最常用的方式,與同步請求從方法來來比較:excute()、enqueue(Callback)
Call<ResponseBean > responseBean = sendAuthCodeService.sendAuthCode();
responseBean.enqueue(new Callback<ResponseBean>(){
@Override
public void onResponse(Call<ResponseBean> call, Response<ResponseBean> response) {
}
@Override
public void onFailure(Call<ResponseBean> call, Throwable t) {
}
});
如果你需要最原始的響應內容而不是經過映射後的數據,可以通過onResponse()response參數獲取(一般情況下用不到)
Response raw = response.raw();
請求參數使用@Body註解發送Object
請求的Body部分不再像以前那樣直接key value 這樣直接設置鍵值對,而是把請求參數封裝成對象,使用註解 @Body
public interface TaskService {
@POST("/tasks")
Call<Task> createTask(@Body Task task);
}
public class Task {
private long id;
private String text;
public Task(long id, String text) {
this.id = id;
this.text = text;
}
}
Task task = new Task(1, "my task title");
Call<Task> call = taskService.createTask(task);
call.enqueue(new Callback<Task>() {});
上面這種方式在某種情況下,你會發現請求不成功,一直提示你請求參數格式有問題,這時候可能是服務器端的api可能僅僅支持form表單提交,這裏需要@FieldMap註解標籤,把對象轉換一下
響應參數映射 轉換器
請求響應內容JSON數據得映射轉換,Retrofit提供了幾種,最常用的就是GSON了(Jackson效率最高)
這塊我使用的就是GsonConverterFactory,這裏需要注意一下,如果你compile的版本是2.0及以下版本是沒有這個類的,我用的2.1.0.當然你也可以自定義Converter轉換器.
retrofit = new Retrofit.Builder()
.baseUrl(Constact.BASE_URL)
.callFactory(OkhttpHelper.getOkhttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
添加請求頭部
爲請求添加請求頭,主要方案有一下三種
靜態請求頭
動態請求頭
請求頭攔截器
添加頭部請求頭靜態的方式也分爲兩種:單一請求頭和多個請求頭
public interface UserService {
@Headers("Cache-Control: max-age=640000")
@GET("/tasks")
Call<List<Task>> getTasks();
}
public interface UserService {
@Headers({
"Accept: application/vnd.yourapi.v1.full+json",
"User-Agent: Your-App-Name"
})
@GET("/tasks/{task_id}")
Call<Task> getTask(@Path("task_id") long taskId);
}
添加攔截器,通過攔截請求添加請求頭
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("User-Agent", "Your-App-Name")
.header("Accept", "application/vnd.yourapi.v1.full+json")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
添加動態請求頭
public interface UserService {
@GET("/tasks")
Call<List<Task>> getTasks(@Header("Content-Range") String contentRange);
}
Retrofit2 與Retrofit1添加請求頭截然不同,如果你想使用Retrofit1請忽略這篇blog.
@Query 註解的可選參數
根據查詢條件獲取請求結果,使用@GET請求註解+@Query/@QueryMap
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks(@Query("sort") String order);
}
如果你使用的 BaseUrl https://your.api.com,調用上面接口的方法,傳入參數值=1,拼接後的請求路徑就是:
https://your.api.com/tasks?sort=1
註解@Query的參數支持多種類型:int, float, long等
public interface TaskService {
@GET("/tasks")
Call<List<Task>> getTasks(
@Query("sort") String order,
@Query("page") Integer page);
}
How to Integrate XML Converter?關於這個xml解析這塊略過了(一般情況下不會用到,都JSON+GSON),使用也挺簡單一個套路。
Debug 調試請求,打印log日誌
在開發中打印網絡請求和返回結果是非常重要的,如果你在Retrofit中使用它,你會發現實現該功能的方法已經不可用了。
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(API_LOCATION)
.setLogLevel(RestAdapter.LogLevel.FULL) // this is the important line
.setClient(new OkClient(new OkHttpClient()));
Retrofit 依靠okhttp提供的日誌系統,HttpLoggingInterceptor.需要添加相關依賴,使用類似上面提到的添加頭部攔截器一樣。
個人更喜歡使用Logger庫配合輸出,所以自定義HttpLoggingInterceptor最好。修改HttpLoggingInterceptor.Logger接口類名定義和log方法調用即可
public interface Logger {
void log(String message);
/** A {@link Logger} defaults output appropriate for the current platform. */
Logger DEFAULT = new Logger() {
@Override public void log(String message) {
Platform.get().log(INFO, message, null);
}
};
}
如何上傳文件到服務器
文件上傳有很多種方式,先大致列舉一二
File轉String上傳
文件流上傳
byte字節上傳
文件上傳
上傳的文件又分爲單一文件和多個文件,在Retrofit下面又該如何編碼呢?按照流程還是的定義interface,@Multipart註解方法,@Part註解參數
/**
* Created by idea on 2016/11/28.
*/
public interface ApiService {
@Multipart
@POST("/upload")
Call<ResponseBean> uploadPhone(@Part("description") RequestBody requestBody,@Part MultipartBody.Part part);
}
RequestBody在這裏進行一下拓展,RequestBody源自Okhttp3的一個類,writeTo方法依賴於Okio框架,這塊知識有想要了解的自行補腦。
MediaType這裏列舉三類
text/plain; charset=utf-8 //構建字符串請求體
application/octet-stream //構建字節請求體 、文件請求體
application/json; charset=utf-8 //post上傳json
……………
通過調用RequestBody定義好的create方法即可。當然獲取RequestBody實例方法不止這一種,大致可分爲三類
- create()
/**
* 此方法在使用Retrofit基本不會用到 post參數傳入
*/
public Request getRequest(){
Request request = new Request.Builder()
.url(baseUrl)
.post()
.build();
return request;
}
- FormBody.Builder()
/**
* 請求表單構建
*/
public RequestBody getRequestBody(){
RequestBody formBody=new FormBody.Builder()
.add("name","idea")
.add("sex","1")
.build();
return request;
}
- MultipartBody.Builder()
/**
* MultipartBody.builder構建RequestBody
*/
public void getRequestBody(String imageUrl,String uuid){
RequestBody multipartBody=new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("uuid", uuid)
.addFormDataPart("avatar", "20160808324.jpg", RequestBody.create(MediaType.parse("application/octet-stream"),new File(imageUrl)))
.addPart(...)
.build();
return multipartBody;
}
addPart(..)方法用於添加RequestBody,Headers和添加自定義Part
下面是一段上傳文件的代碼塊
public void onUploadImage(String imageUrl,String description,ApiService apiService){
File file = new File(imageUrl);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
RequestBody descriptionRequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), description);
Call<ResponseBean> call = apiService.uploadPhone(descriptionRequestBody,body);
call.enqueue(new Callback<ResponseBean>() {
@Override
public void onResponse(Call<ResponseBean> call,Response<String> response) {
}
@Override
public void onFailure(Call<ResponseBean> call, Throwable t) {
}
});
}
這裏使用multipart/form-data,它和其他有什麼區別呢?
1.application/x-www-form-urlencoded
這是通過表單發送數據時默認的編碼類型。我們沒有在from標籤中設置enctype屬性時默認就是application/x-www-form-urlencoded類型的。application/x-www-form-urlencoded編碼類型會把表單中發送的數據編碼爲名稱/值對。這是標準的編碼格式。當表單的ACTION爲POST的時候,瀏覽器把form數據封裝到http body中,然後發送到服務器。當表單的ACTION爲GET的時候,application/x-www-form-urlencoded編碼類型會把表單中發送的數據轉換成一個字符串(name=coderbolg&key=php),然後把這個字符串附加到URL後面,並用?分割,接着就請求這個新的URL。
2.multipart/form-data
這個是專門用來傳輸特殊類型數據的,如我們上傳的非文本的內容,比如圖片或者MP3等。multipart/form-data編碼類型會把表單中的發送的數據編碼爲一條消息,頁面上每個表單控件對應消息中的一部分。當表單中有file類型控件並希望它正常工作的話(廢話吧)就必須設置成multipart/form-data類型,瀏覽器會把整個表單以控件爲單位分割,併爲每個部分加上Content-Disposition(form-data或者file),Content-Type(默認爲text/plain),name(控件 name)等信息,並加上分割符(boundary)。
3.text/plain
數據以純文本形式進行編碼,其中不含任何控件或格式字符。
以上資料三點摘自:http://www.fx114.net/qa-163-89638.aspx,平時上傳文件使用multipart/form-data基本沒問題,如果你想知道更多的HTTP Content-Type相關信息,可以參考下面鏈接
// http://tool.oschina.net/commons
Retrofit 2 與1.9的差異
Retrofit2與之前版本的具體差異變化自行參考(idea沒用過以以前版本的直接2.1.0)
// https://futurestud.io/tutorials/retrofit-2-upgrade-guide-from-1-9
這裏補充一點:假設項目你自己引入了高版本的Okhttp versionA,而使用的Retrofit2.x版本底層依賴的Okhttp的版本號versionB,明顯Okhttp重複引用,如果versionB 小於你當前引入versionA,當你刪除了versionA會發現method找不到等一些列問題,最好的解決辦法就是把Retrofit2依賴庫改成你當前compile的庫
compile ('com.squareup.retrofit2:retrofit:2.1.0') {
// exclude Retrofit’s OkHttp peer-dependency module and define your own module import
exclude module: 'okhttp'
}
compile 'com.squareup.okhttp3:okhttp:3.4.1'
Retrofit 2 錯誤異常處理
首先,我們創建一個錯誤對象,官方給出的ErrorUtils對個人來講並不太完美,我還是比較喜歡這樣
{
code: 404,
msg: "服務器連接失敗"
}
/**
* Created by idea on 2016/11/9.
*/
public class AppErrorCode {
/**
* 未知錯誤
*/
public static final int UNKNOWN = 1000;
/**
* 解析錯誤
*/
public static final int PARSE_ERROR = 1001;
/**
* 網絡錯誤
*/
public static final int NETWORK_ERROR = 1002;
/**
* 協議出錯
*/
public static final int HTTP_ERROR = 1003;
}
/**
* Created by idea on 2016/11/9.
*/
public class AppException extends Exception {
private int code;
private String msg;
public AppException(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
public static AppException handleException(Throwable e) {
if (e instanceof JsonParseException
|| e instanceof IOException
|| e instanceof JSONException
|| e instanceof ParseException) {
return new ApiException(AppErrorCode.PARSE_ERROR, "解析錯誤");
} else if (e instanceof ConnectException) {
return new ApiException(AppErrorCode.NETWORK_ERROR, "連接失敗");
} else if(){
//.............略......................
}else{
return new ApiException(AppErrorCode.UNKNOWN, "未知錯誤");
}
}
}
舉個例子,當你convert response數據發生io異常是,只需要這樣
try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
return AppException.handleException(e);
}
表單提交@Field @FieldMap
表單提交可能存在一個key對應一個或多個值的情況,在Retrofit中編碼如下
public interface TaskService {
@FormUrlEncoded
@POST("tasks")
Call<Task> createTask(@Field("title") String title);
}
public interface TaskService {
@FormUrlEncoded
@POST("tasks")
Call<List<Task>> createTasks(@Field("title") List<String> titles);
}
當你的參數@Field參數過多了,可以嘗試用@FieldMap
public interface UserService {
@FormUrlEncoded
@PUT("user")
Call<User> update(@FieldMap Map<String, String> fields);
}
Retrofit 2 怎麼爲每個請求都添加 Query Parameters
第一個想到的就應該是攔截器了,intercept回調方法addQueryParameter
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
HttpUrl originalHttpUrl = original.url();
HttpUrl url = originalHttpUrl.newBuilder()
.addQueryParameter("apikey", "your-actual-api-key")
.build();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.url(url);
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
Retrofit 2 Url語法
個人覺得這裏沒什麼好理解,參考實例看看就明白了
Retrofit 2 如何從服務器下載文件
從服務器下載文件主要分爲以下幾個步湊(個人更喜歡用開源庫進行下載,一般採用三級緩存+動態權限)
- 如何改造Request
// option 1: a resource relative to your base URL
@GET("/resource/example.zip")
Call<ResponseBody> downloadFileWithFixedUrl();
// option 2: using a dynamic URL
@GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);
- 如何調用請求
FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccess()) {
Log.d(TAG, "server contacted and has file");
boolean writtenToDisk = writeResponseBodyToDisk(response.body());
Log.d(TAG, "file download was a success? " + writtenToDisk);
} else {
Log.d(TAG, "server contact failed");
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, "error");
}
});
- 如何緩存文件
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
// todo change the file location/name according to your needs
File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + "Future Studio Icon.png");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}
- 下載大文件使用@Streaming
@Streaming
@GET
Call<ResponseBody> downloadFileWithDynamicUrlAsync(@Url String fileUrl);
final FileDownloadService downloadService =
ServiceGenerator.create(FileDownloadService.class);
new AsyncTask<Void, Long, Void>() {
@Override
protected Void doInBackground(Void... voids) {
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccess()) {
Log.d(TAG, "server contacted and has file");
boolean writtenToDisk = writeResponseBodyToDisk(response.body());
Log.d(TAG, "file download was a success? " + writtenToDisk);
}
else {
Log.d(TAG, "server contact failed");
}
}
return null;
}
}.execute();
Retrofit 2 — 取消 Requests
當前界面在請求中,突然要結束當前界面亦或者是有新的請求進來,需要取消之前的請求,此時可以調用Call提供的方法
new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, "request success");
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if(!call.isCanceled()){
call.cancel();
}
//..........................
}
};
Retrofit 2如何在運行時動態改變BaseUrl
一般情況下我們使用Retrofit2時都會進行一下封裝,把BaseUrl封裝進去。在某種情況下(上傳文件服務器和表單提交服務器不在一起),就需要我們改變BaseUrl,具體做法如下changeApiBaseUrl()
public class ServiceGenerator {
public static String apiBaseUrl = "http://futurestud.io/api";
private static Retrofit retrofit;
private static Retrofit.Builder builder =
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiBaseUrl);
private static OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
// No need to instantiate this class.
private ServiceGenerator() {
}
public static void changeApiBaseUrl(String newApiBaseUrl) {
apiBaseUrl = newApiBaseUrl;
builder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(apiBaseUrl);
}
public static <S> S createService(Class<S> serviceClass, AccessToken token) {
String authToken = token.getTokenType().concat(token.getAccessToken());
return createService(serviceClass, authToken);
}
// more methods
// ...
}
請求的Path Parameters
如果你的url的路徑參數是正確的,但註解的路徑參數含有一個空字符串會導致錯誤的請求url
public interface TaskService {
@GET("tasks/{taskId}/subtasks")
Call<List<Task>> getSubTasks(@Path("taskId") String taskId);
}
taskId傳遞空值將導致以下url
// https://your.api.url/tasks//subtasks
不允許您傳遞null作爲路徑參數的值,如果你這樣做,就會拋出IllegalArgumentException
Retrofit 2響應內容爲String
有那麼一羣人不喜歡使用Gson、Jackson解析,喜歡自己封裝一套解析工具,那麼返回值需要爲String,我們就需要添加返回值的支持(返回他不支持的結果時,就會崩潰)
compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
private static Retrofit getRetrofit(String url) {
return new Retrofit.Builder().baseUrl(url)
//增加返回值爲String的支持
.addConverterFactory(ScalarsConverterFactory.create())
//增加返回值爲Gson的支持(以實體類返回)
.addConverterFactory(GsonConverterFactory.create())
//增加返回值爲Oservable<T>的支持
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
使用方面的知識點大概就這麼多了,以上內容多數參照https://futurestud.io/tutorials/retrofit-getting-started-and-android-client這裏,看完之後做個記錄,收穫還是不小的。