Retrofit学习笔记
*翻译自官方文档(https://square.github.io/retrofit/)
QuickstartRetrofit主要做的事情就是把HTTP API转换成Java接口。
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit
类产生一个GitHubService
接口的实现。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
从GitHubService
中产生的每个Call
对象都可以用来向远程Web服务器产生一个同步或异步的HTTP请求。
请求方法
支持HTTP
、GET
、POST
等所有HTTP方法。
@GET("users/list")
// 加上请求参数
@GET("users/list?sort=desc")
URL填充
支持动态URL,可以通过{}
来包裹需要填充的字符串(数字和字母)。在参数中用@Path
来指定。
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
也支持请求参数(用@Query
指定)
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
在复杂查询中可以使用Map(用@QueryMap
指定)
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
请求体(Body)
用@Body
指定request body。
@POST("users/new")
Call<User> createUser(@Body User user);
该对象会根据Retrofit
实例中指定的converter转换。(解释:例如如果指定使用Gson,就会使用Gson对该对象进行转换,即转换成json。)如果没有指定converter,只能使用RequestBody
。(?)
表单和multipart
使用@FormUrlEncoded
和@Multipart
。如
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
这部分用得比较少,不细看了。关于Multipart和Form-urlencoded的区别可以看https://blog.csdn.net/lihefei_coder/article/details/99606386
请求头
用@Header
标识。
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
请求头也可以是动态的。当值为空时,会忽略该请求头。否则将会对其值调用toString
。
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
也可以使用Map
@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)
同步和异步
Call
实例可以同步或异步执行,每个实例只能使用一次,但clone()
会产生一个可以使用的新实例。
在安卓中 ,回调会在主线程中执行。在JVM中,回调会在执行请求的同一个线程中执行。
Retrofit配置使用Gson:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
第一行代码中的实例(SunnyWeather)目录结构
其中PlaceService、WeatherService是绑定Retrofit的接口,ServiceCreator用于创建对应Service的retrofit实例,SunnyWeatherNetwort则是供Repository调用的方法。
PlaceService
WeatherService和PlaceService类似,不重复讲了。
package com.sunnyweather.android.logic.network
import com.sunnyweather.android.SunnyWeatherApplication
import com.sunnyweather.android.logic.model.PlaceResponse
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
interface PlaceService {
@GET("v2/place?token=${SunnyWeatherApplication.TOKEN}&lang=zh_CN")
fun searchPlaces(@Query("query") query: String): Call<PlaceResponse>
}
主要是用GET方法,${SunnyWeatherApplication.TOKEN}
表示从配置文件中取token的值。
ServiceCreator
用来创建Retrofit实例。
package com.sunnyweather.android.logic.network
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ServiceCreator {
private const val BASE_URL = "https://api.caiyunapp.com/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
inline fun <reified T> create(): T = create(T::class.java)
}
inline那一行是泛型优化,暂时没搞懂。
SunnyWeatherNetwork
主要用了挂起函数(suspend)实现异步。
package com.sunnyweather.android.logic.network
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
object SunnyWeatherNetwork {
private val weatherService = ServiceCreator.create(WeatherService::class.java)
suspend fun getDailyWeather(lng: String, lat: String) = weatherService.getDailyWeather(lng, lat).await()
suspend fun getRealtimeWeather(lng: String, lat: String) = weatherService.getRealtimeWeather(lng, lat).await()
private val placeService = ServiceCreator.create(PlaceService::class.java)
suspend fun searchPlaces(query: String) = placeService.searchPlaces(query).await()
private suspend fun <T> Call<T>.await(): T {
return suspendCoroutine { continuation ->
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
val body = response.body()
if (body != null) continuation.resume(body)
else continuation.resumeWithException(RuntimeException("response body is null"))
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
}