什么是Retrofit
retrofit是现在比较流行的网络请求框架,可以理解为okhttp的加强版,底层封装了Okhttp。准确来说,Retrofit是一个RESTful的http网络请求框架的封装。因为网络请求工作本质上是由okhttp来完成,而Retrofit负责网络请求接口的封装。
本质过程:App应用程序通过Retrofit请求网络,实质上是使用Retrofit接口层封装请求参数、Header、Url等信息,之后由okhttp来完成后续的请求工作。在服务端返回数据后,okhttp将原始数据交给Retrofit,Retrofit根据用户需求解析。
使用介绍
使用 Retrofit 的步骤共有7个:
步骤1:添加Retrofit库的依赖
步骤2:创建 接收服务器返回数据 的类
步骤3:创建 用于描述网络请求 的接口
步骤4:创建 Retrofit 实例
封装了 数据转换、线程切换的操作
步骤5:处理服务器返回的数据
1.添加依赖
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
由于Retrofit是基于OkHttp开发的,因此添加上述第一条依赖会自动将Retrofit、OkHttp和 Okio这几个库一起下载,我们无须再手动引入OkHttp库。
另外,Retrofit还会将服务器返回的 JSON数据自动解析成对象,因此上述第二条依赖就是一个Retrofit的转换库,它是借助GSON来解析JSON数据的,所以会自动将GSON库一起下载下来,这样我们也不用手动引入GSON库了。除了GSON之外,Retrofit还支持各种其他主流的JSON解析库,包括Jackson、Moshi等,不过毫无疑问GSON是最常用的。
2.创建接收服务器返回数据的类
class App(val id: String, val name: String, val version: String)
3.创建用于描述网络请求的接口
interface AppService {
@GET("get_data.json")
fun getAppData():Call<List<App>>
}
上述代码中有两点需要我们注意。第一就是在getAppData()方法上面添加的注解,这里使用了一个@GET注解,表示当调用getAppData()方法时Retrofit会发起一条GET请求,请求的地址就是我们在@GET注解中传入的具体参数。注意,这里只需要传入请求地址的相对路径即可,根路径我们会在稍后设置。
第二就是getAppData()方法的返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象。由于服务器响应的是一个包含App数据的JSON数组,因此这里我们将泛型声明成List<App>。当然,Retrofit还提供了强大的Call Adapters功能来允许我们自定义方法返回值的类型,比如Retrofit结合Rxjava使用就可以将返回值声明成 Observable、Flowable等类型,不过这些内容就不在本节的讨论范围内了。
4.使用Retrofit
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getAppDataBtn.setOnClickListener {
val retrofit = Retrofit.Builder()
.baseUrl("http://10.0.2.2/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val appService = retrofit.create(AppService::class.java)
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()
}
})
}
}
}
可以看到,在“Get App Data”按钮的点击事件当中,首先使用了Retrofit.Builder来构建一个Retrofit对象,其中baseUrl()方法用于指定所有Retrofit请求的根路径,
addConverterFactory()方法用于指定Retrofit在解析数据时所使用的转换库,这里指定成 GsonConverterFactory。注意这两个方法都是必须调用的。
有了Retrofit对象之后,我们就可以调用它的create()方法,并传入具体Service接口所对应的Class类型,创建一个该接口的动态代理对象。如果你并不熟悉什么是动态代理也没有关系,你只需要知道有了动态代理对象之后,我们就可以随意调用接口中定义的所有方法,而 Retrofit会自动执行具体的处理就可以了。
使用时别忘了申请网络权限哦,
<!-- 访问网络的权限 -->
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
使用时可能会遇到以下问题:
CLEARTEXT communication to mock-api.com not permitted by network security policy
这是因为Android P
不允许明文访问,而前面的mock
地址是http
开头的,解决办法是在AndroidManifest
中的application
内加入下面这段代码即可:
android:usesCleartextTraffic="true"
以上就是Retrofit最基本的一次网络请求,但是显然,服务器不可能一直给我们提供静态类型接口,很多情况下,接口中的部分内容很可能会动态变化。接下来我们详细看下Retrofit网络请求接口中主要有哪些注解的使用。
Get请求
假设baseUrl为:http://mock-api.com/2vKVbXK8.mock/
@Query
访问地址
http://mock-api.com/2vKVbXK8.mock/api/getUserInfo?id=1234
//1234为传来的参数
@GET("api/getUserInfo")
fun getUserInfo(@Query("id") userId : String):Call<App>
复制代码
如果我们要访问的地址为:baseUrl
+getUserInfo?id=1234
,以?
形式拼接一个参数这种格式时,就使用@Query
注解,该注解就是在getUserInfo后面添加?
,并且以id=传来的参数userId
的形式拼接url
@QueryMap
访问地址
http://mock-api.com/2vKVbXK8.mock/api/getArticalInfo?id=405&page=1
实例
@GET("api/getArticalInfo")
fun getUserInfo(@QueryMap params : Map<String, String>) : Call<App>
当要访问的地址是通过?
形式拼接多个参数时就使用@QueryMap
注解
@Path
访问地址
http://mock-api.com/2vKVbXK8.mock/api/getDynamicInfo/1/data
http://mock-api.com/2vKVbXK8.mock/api/getDynamicInfo/2/data
实例
@GET("api/getDynamicInfo/{param}/data")
fun getDynamicInfo(@Path("param") param : int) : Call<App>
当要访问的地址由某个参数动态拼接而成时,使用@Path
注解,上面实例中param这里具体填入的内容是后面调用该方法时传入的参数
@URl
@GET
fun getDynamicUrl(@Url url : String) : Call<ResponseBody>
当要访问的地址不只是动态的变几个参数,而是整个地址都要变化,甚至是基类地址也要变化时,这种动态地址就要用到@Url注解
@Body
可以看到@FieldMap
注解适合多个相同类型参数的传递,如果多个不同类型传递的话,总不能写多个@Field
吧
Call<ResponseBody> postFieldFun(@Field("key") String key,@Field("num")int num);
如果要更多种类型的话那就更繁琐了,所以这里我们可以用@Body
注解,直接传入一个对象过去,对象内可包含多种类型数据。
@POST("api/bodyParam")
fun postBodyFun(@Body postBodyBean : PostBodyBean) :Call<ResponseBody>
本文参考自郭霖《Android第一行代码》第三版
参考博客:【精选】android_框架_Retrofit使用详解_retrofit map_写bug的蚊子的博客-CSDN博客