本文使用登錄場景來簡單介紹 Android 應(yīng)用中使用 OkHttp + Retrofit 訪問網(wǎng)絡(luò)的用法氢哮。
- 數(shù)據(jù)交換協(xié)議 HTTP
- 數(shù)據(jù)交換格式 JSON
- HTTP 請求方法 POST
使用 HTTP 訪問網(wǎng)絡(luò)的準(zhǔn)備工作固以,參見Android 訪問網(wǎng)絡(luò):方案一 OkHttp一文訪問網(wǎng)絡(luò)的準(zhǔn)備工作部分。
使用 OkHttp + Retrofit 訪問網(wǎng)絡(luò)
1.引入依賴
build.gradle(:app)
dependencies {
...
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.9'
}
2.定義網(wǎng)絡(luò)訪問接口
創(chuàng)建 UserService 接口汹胃,用來提供 User 模塊的網(wǎng)絡(luò)訪問服務(wù)。除了登錄 login() , 還可以在這里定義獲取用戶信息焦人,修改用戶名竖独,更換頭像等功能對應(yīng)的方法。
UserService.java
public interface UserService {
@POST("user/login")
Call<Result<String>> login(@Body LoginParam loginParam);
// getUserDetail()
// modifyUserName()
// replaceAvatar()
}
方法注解 @POST
- 方法注解用于描述該請求的 HTTP 方法绷杜,@POST 表示該請求的方法為 POST 直秆。
- "user/login" 是請求的相對路徑,Retrofit 會將其和 baseUrl 拼接起來鞭盟,構(gòu)成完整的 URL 圾结。
- 比較常用的其他方法注解
@GET
@PUT
@DELETE
方法參數(shù)注解 @Body
- 方法參數(shù)注解用于描述方法參數(shù)作為請求的什么內(nèi)容,@Body 表示把方法參數(shù)的序列化結(jié)果作為請求體齿诉。
- 比較常用的其他方法參數(shù)注解
@Field 表示采用鍵值對的形式組織請求參數(shù)筝野,作為請求體。使用該注解時(shí)粤剧,需要為方法添加 @FormUrlEncoded 注解歇竟,設(shè)置 MIME type 為 application/x-www-urlencoded 。
@Field 使用示例
方法定義
@FormUrlEncoded
@POST("/")
Call<ResponseBody> example(
@Field("name") String name,
@Field("occupation") String occupation);方法調(diào)用 foo.example("Bob Smith", "President")
請求體 name=Bob+Smith&occupation=President
注:多個(gè)請求參數(shù)使用 & 分隔抵恋,每個(gè)參數(shù)的名和值使用 = 連接焕议。"Bob Smith" 的空格替換為 + 。
LoginParam
LoginParam 是簡單的登錄請求參數(shù)類弧关,包含用戶名和密碼兩個(gè)屬性盅安。
LoginParam.java
public class LoginParam {
public String userName;
public String password;
public LoginParam(String userName, String password){
this.userName = userName;
this.password = password;
}
}
Result
Result 是網(wǎng)絡(luò)訪問結(jié)果類,包含 code, message, data 三個(gè)屬性世囊。
Result.java
public class Result<DATA> {
private static final int HTTP_REQUEST_SUCCESS_CODE = 600;
public int code;
public String message;
public DATA data;
public boolean isSuccessful() {
return code == HTTP_REQUEST_SUCCESS_CODE;
}
@NonNull
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
3. 創(chuàng)建 Retrofit 實(shí)例
- 創(chuàng)建 OkHttp 實(shí)例别瞭,添加日志攔截器,以便調(diào)試株憾。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
- 創(chuàng)建 Retrofit 實(shí)例蝙寨,配置 baseUrl, 添加 ConverterFactory晒衩。
Retrofit retrofit = new Retrofit.Builder().
baseUrl("http://192.168.43.218:8080/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
4. 創(chuàng)建 Service 實(shí)例
UserService userService = retrofit.create(UserService.class);
5. 創(chuàng)建 Call 實(shí)例
Call<Result<String>> loginCall = userService.login(new LoginParam(userName, password));
假如這里 userName 的值為 "xumeng",password 的值為 "123456"墙歪,Retrofit 會使用 GsonConverter 將 LoginParam 對象轉(zhuǎn)為 JSON 字符串作為請求體听系。
請求體
{"password":"123456","userName":"xumeng"}
由于這一步比較簡單,通常的寫法是和下一步連起來箱亿,將 Call 的實(shí)例作為匿名對象來使用跛锌。
userService.login(new LoginParam(userName, password)).enqueue(new Callback<Result<String>>(){...});
6. 向服務(wù)端發(fā)送 HTTP 請求
- Call 可以調(diào)用
execute()
發(fā)起同步請求,也可以調(diào)用enqueue(Callback<T>)
發(fā)起異步請求届惋。本文采用異步方式發(fā)送請求髓帽。
Call 實(shí)例入隊(duì),創(chuàng)建 Callback 實(shí)例脑豹,用于處理網(wǎng)絡(luò)訪問結(jié)果
loginCall.enqueue(new Callback<Result<String>>() {
@Override
public void onResponse(@NonNull Call<Result<String>> call, @NonNull Response<Result<String>> response) {
}
@Override
public void onFailure(@NonNull Call<Result<String>> call, @NonNull Throwable t) {
}
});
7. 在 Callback 的 onResponse() 中處理網(wǎng)絡(luò)訪問結(jié)果
- Retrofit 會為我們從后臺線程切換到主線程郑藏,所以在 onResponse() 方法中無需考慮線程切換的問題。
- Retrofit 會使用我們配置的 ConverterFactory 將響應(yīng)體的 JSON 數(shù)據(jù)轉(zhuǎn)換為
Result<>
類的實(shí)例瘩欺,調(diào)用response.body()
方法即可獲取必盖。
@Override
public void onResponse(@NonNull Call<Result<String>> call, @NonNull Response<Result<String>> response) {
Result<String> result = response.body();
if (result == null) {
onFailure(call, new Throwable("body is null"));
return;
}
Toast.makeText(LoginActivity.this, result.message, Toast.LENGTH_SHORT).show();
if (result.isSuccessful()) {
Log.i(TAG, "token : " + result.data);
// save token
// start activity
}
}
附
測試設(shè)備參數(shù)
- 型號:vivo Y66L
- 操作系統(tǒng):Funtouch OS 3.0(Android 6.0.1)