需要先在项目中添加必要的依赖库。编辑app/build.gradle文件,在dependencies闭包中添加如下内容:
dependencies {
...
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
}
使用举例
1.新增一个实体类App类
data class App(val id: String, val name: String, val version: String)
2.定义一个接口文件,新建AppService接口
interface AppService {
@GET("get_data.json")
fun getAppData(): Call<List<App>>
}
上述代码中有两点需要我们注意。第一就是在getAppData()方法上面添加的注解,这里使用了一个@GET注解,表示当调用getAppData()方法时Retrofit会发起一条GET请求,请求的地址就是我们在@GET注解中传入的具体参数。注意,这里只需要传入请求地址的相对路径即可,根路径我们会在稍后设置。
第二就是getAppData()方法的返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象。由于服务器响应的是一个包含App数据的JSON数组,因此这里我们将泛型声明成List。当然,Retrofit还提供了强大的Call Adapters功能来允许我们自定义方法返回值的类型。
3.在activity_main.xml中定义一个按钮来测试Retrofit网络请求事件,我们在它的点击事件中处理具体的网络请求逻辑即可。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/getAppDataBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Get App Data" />
</LinearLayout>
现在修改MainActivity中的代码:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getAppDataBtn.setOnClickListener {
//来构建一个Retrofit对象
//baseUrl用于指定所有Retrofit请求的根路径
val retrofit = Retrofit.Builder().baseUrl("url")
//指定Retrofit在解析数据时所使用的转换库,这里指定成GsonConverterFactory
.addConverterFactory(GsonConverterFactory.create())
.build()
//创建一个该接口的动态代理对象
val appService = retrofit.create(AppService::class.java)
//当调用了AppService的getAppData()方法时,会返回一个Call<List<App>>对象,
// 这时我们再调用一下它的enqueue()方法,Retrofit就会根据注解中配置的服务器接口地址去进行网络请求了,
// 服务器响应的数据会回调到enqueue()方法中传入的Callback实现里面。
//当发起请求的时候,Retrofit会自动在内部开启子线程,当数据回调到Callback中之后,
// Retrofit又会自动切换回主线程,整个操作过程中我们都不用考虑线程切换问题。
appService.getAppData().enqueue(object : Callback<List<App>> {
//成功返回数据的情况
override fun onResponse(call: Call<List<App>>, response: Response<List<App>>) {
val list = response.body()
if (list != null) {
for (app in list) {
Log.d("MainActivity", "id is ${app.id}")
Log.d("MainActivity", "name is ${app.name}")
Log.d("MainActivity", "version is ${app.version}")
}
}
}
//失败的情况
override fun onFailure(call: Call<List<App>>, t: Throwable) {
t.printStackTrace()
}
})
}
}
}
4.处理复杂的接口地址类型
首先先定义一个实体类Data
data class Data(val id: String, val content: String)
情况一:接口地址是静态的,永远不会改变。那么对应到Retrofit当中,使用如下的写法即可:
GET http://example.com/get_data.json
interface ExampleService {
@GET("get_data.json")
fun getData(): Call<Data>
}
情况二:在很多场景下,接口地址中的部分内容可能会是动态变化的。当该方法发起请求时,Retrofit就会自动将page参数的值替换到占位符的位置,从而组成一个合法的请求地址。
GET http://example.com/<page>/get_data.json
interface ExampleService {
@GET("{page}/get_data.json")
fun getData(@Path("page") page: Int): Call<Data>
}
情况三:当我们需要传入一系列参数时:这是一种标准的带参数GET请求的格式。接口地址的最后使用问号来连接参数部分,每个参数都是一个使用等号连接的键值对,多个参数之间使用“&”符号进行分隔。
GET http://example.com/get_data.json?u=<user>&t=<token>
interface ExampleService {
@GET("get_data.json")
fun getData(@Query("u") user: String, @Query("t") token: String): Call<Data>
}
这里在getData()方法中添加了user和token这两个参数,并使用@Query注解对它们进行声明。这样当发起网络请求的时候,Retrofit就会自动按照带参数GET请求的格式将这两个参数构建到请求地址当中。
情况四:Retrofit对所有常用的HTTP请求类型都进行了支持,使用@GET、@POST、@PUT、@PATCH、@DELETE注解,就可以让Retrofit发出相应类型的请求了。
DELETE http://example.com/data/<id>
interface ExampleService {
//由于POST、PUT 、PATCH、DELETE这几种请求类型与GET请求不同,它们更多是用于操作服务器上的数
//据,而不是获取服务器上的数据,所以通常它们对于服务器响应的数据并不关心。这个时候就可以使用
//ResponseBody,表示Retrofit能够接收任意类型的响应数据,并且不会对响应数据进行解析。
@DELETE("data/{id}")
fun deleteData(@Path("id") id: String): Call<ResponseBody>
}
情况五:如果我们需要向服务器提交数据该怎么写呢?
POST http://example.com/data/create
{"id": 1, "content": "The description for this data."}
interface ExampleService {
//使用POST请求来提交数据,需要将数据放到HTTP请求的body部分,这个功能在Retrofit中可以借助@Body注解来完成:这样当Retrofit发出POST请求时,就会自动将Data对象中的数据转换成JSON格式的文本,并放到HTTP请求的body部分,服务器在收到请求之后只需要从body中将这部分数据解析出来即可。这种写法同样也可以用来给PUT、PATCH、DELETE类型的请求提交数据。
@POST("data/create")
fun createData(@Body data: Data): Call<ResponseBody>
}
情况六:有些服务器接口还可能会要求我们在HTTP请求的header中指定参数
GET http://example.com/get_data.json
User-Agent: okhttp
Cache-Control: max-age=0
interface ExampleService {
@Headers("User-Agent: okhttp", "Cache-Control: max-age=0")
@GET("get_data.json")
fun getData(): Call<Data>
}
//如果想要动态指定header的值
interface ExampleService {
//现在当发起网络请求的时候,Retrofit就会自动将参数中传入的值设置到
//User-Agent和Cache-Control这两个header当中,从而实现了动态指定header值的功能。
@GET("get_data.json")
fun getData(@Header("User-Agent") userAgent: String,
@Header("Cache-Control") cacheControl: String): Call<Data>
}
//retrofit 最佳写法
object RetrofitUtil {
val retrofit = Retrofit.Builder().baseUrl("127.0.0.1:8080")
.addConverterFactory(GsonConverterFactory.create()).build()
fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
inline fun <reified T> create(): T = create(T::class.java)
}