网络 - Retrofit

本文介绍如何在Android应用中使用Retrofit 2.9.0进行网络请求,包括创建Retrofit实例、定义实体类、接口注解、添加依赖及解决HTTP链接安全问题。同时探讨了OkHttp和Gson的配合,以及如何处理不同类型的网络操作和请求头。

一、概念

        是由Square公司开发的一款 RESTful API 请求框架,它通过注解将HTTP请求抽象为Java/Kotlin接口,简化了网络请求的定义与调用,其核心价值是将网络请求的“配置逻辑”(如URL、请求方法、参数)与“执行逻辑”解耦,让代码更简洁、可维护。

        Retrofit本身不执行网络请求,而是将接口定义的请求参数转换为 OkHttp 可识别的 Request 对象,最终通过 OkHttp 完成TCP连接、数据传输等底层操作。

HttpClientAndroid 6中移除(API数量多扩展困难)。
HttpURLConnection目前官方集成的。
OKHttpSquare公司出品,底层通讯的实现。
RetrofitSquare公司出品,上层接口的封装(注解代替代码)更方便进行网络请求。

二、基本使用

Retrofit把网络请求的 URL 分成了两部分设置:创建Retrofit实例时通过 .baseUrl("...") 设置的 + 网络访问接口的函数注解 @GET("...") 设置的。

2.1 添加依赖

查看最新版本

implementation 'com.squareup.retrofit2:retrofit:2.9.0'    //会连带下载 OkHttp和Okio
implementation 'com.squareup.retrofit2:converter-gson:2.k6.1'    //JSON序列化库

2.2 构建Retrofit对象

若需为不同接口配置不同的超时/拦截器,需创建多个OkHttpClient实例,再分别初始化多个Retrofit实例(每个Retrofit关联一个OkHttpClient)。

val retrofit = Retrofit.Builder()
    //配置重复的根路径
    .baseUrl("https://www.baidu.com/")
    //关联自定义的OkHttpClient
    .client(okHttpClient)
    //指定解析数据使用的转换库(这里是Gson),使来自接口的json字符串解析成我们定义好的对应bean类
    .addConverterFactory(GsonConverterFactory.create())
    .build()

2.2.1 数据转换接口 Converter

是Retrofit的数据转换接口,负责将HTTP请求的“请求体”(如Java对象)序列化为网络传输的字节流(如JSON字符串),以及将HTTP响应的“响应体”(如JSON字符串)反序列化为Java/Kotlin对象。Android中常用的JSON解析方式可分为原生解析和第三方库解析两类,核心差异在于易用性、性能和功能扩展性。

val gson = GsonBuilder()
    //默认情况下,Gson将Date对象序列化为时间戳,可自定义格式
    .setDateFormat("yyyy-MM-dd HH:mm:ss")
    //下划线命名(如user_name → userName)
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create()

val retrofit = Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create(gson))

手动使用 Gson 类转换

//解析List<T>、Map<K,V>等泛型类型时,需通过TypeToken获取泛型类型(因Java泛型擦除,无法直接用List<User>.class)。
val gson = Gson()    //通过 GsonBuilder 可以更多配置,如为时间戳格式的Date指定时间格式
//反序列化:Json字符串 -> Object实例
val dataBean = gson.fromJson(jsonStr)
//序列化:Object实例 -> Json字符串
val jsonStr = gson.toJson(user)

2.2.2 返回值转换器 CallAdapter

是Retrofit的返回值转换接口,负责将Retrofit默认的Call<T>返回值转换为其他类型(如RxJava的Observable<T>),从而扩展异步请求的实现方式。  Kotlin协程无需额外配置,Retrofit v2.6.0 开始内置 CoroutineCallAdapterFactory。

2.3 定义实体类(数据模型)

根据Json内容,编写对应的实体类。字段名需与Json键名完全一致(大小写敏感),若不一致,需使用@SerializedName注解映射。

data class PersonBean(    //建议类名后缀加Bean与正常类区分
    val name: String = "",    //非空类型+默认值,避免Jon中字段缺失带来null问题
    @SerializedName("age")    //这里与Json键名一致就行
    val a_g_e: Int = 0
    transient val type = false //transient标记的字段不参与序列化/反序列化
)

2.4 定义网络访问接口

  • 根据 API 编写网络访问接口。Retrofit 将 Http 请求抽象成接口,并在接口里面采用注解来配置网络请求参数(每个形参都要注解),用动态代理将该接口“翻译”成一个 Http 请求再执行。
  • 命名通常以功能名称开头+Service结尾。返回值类型必须声明成 Retrofit 内置的 Call 类型,通过泛型指定服务器返回的具体数据类型(使用Call<ResponseBody>则返回没经过Gson转换的原始数据类即json字符串)(使用RxJava声明的是Observable<Person>类型)(使用协程可直接返回对象类型,见下文Android封装用法)。
函数注解说明

@GET

从服务器获取数据。
@POST向服务器提交数据。
@DELETE删除服务器上的数据。

@PUT

@PATCH

修改服务器上的数据。put更新资源、patch部分更新。
@Headers添加固定请求头
参数注解说明
@Header动态添加请求头。
@Path替换路径。
@Query替代参数值(通常结合get请求)。
@Feild替换参数值(通常结合post请求)。
@FormUrlEncoded用于表单数据数据提交。

2.4.1 GET 示例

interface GetService {
    //接口1:https://www.baidu.com/person.json
    @GET("person.json")    //表示发起的是GET请求,传入请求的地址(相对路径,重复根路径在后面配置)
    fun getPerson(): Call<list<Person>>
    //接口2:https://www.baidu.com/<page>/person.json
    @GET("{page}/get_data.json")    //使用 {page} 占位
    fun getData(@Path("page") page: Int): Call<Data>    //使用 @Path("page")注解来声明对应参数
    //接口3:https://www.baidu.com/person.json?u=<user>&t=<token>
    @GET("person.json")
    fun getData(@Query("u") user: String, @Query("t") token: String): Call<Data>
    //接口4:https://api.caiyunapp.com/v2/place?query=北京&token={token}&lang=zh_CN
    @GET("v2/place?token=${GlobalApplication.TOKEN}&lang=zh_CN")    //不变的参数固定写在GET里
    fun searchPlaces(@Query("query") query: String): Call<PlaceResponse>
}

2.4.2 POST 示例

interface PostService {
    //接口6:https://www.baidu.com/data/create{"id": 1, "content": "The description for this data."}
    @POST("data/create")
    fun createData(@Body data: Data): Call<ResponseBody>    //将Data对象中的数据转换成JSON格式的文本,并放到HTTP请求的body部分
}

2.4.3 DELETE 示例

interface DeleteService {
    //接口5:https://www.baidu.com/data/<id>
    @DELETE("data/{id}")
    fun deleteData(@Path("id") id: String): Call<ResponseBody>    //该泛型表示能接受任意类型切不会进行解析
}

2.4.4 Headers 示例

interface PersonService {
    /*接口7:http://example.com/get_data.json
    User-Agent: okhttp    //header参数就是键值对
    Cache-Control: max-age=0
    */

    //静态声明(添加固定请求头)
    @Headers("User-Agent: okhttp", "Cache-Control: max-age=0")
    @GET("get_data.json")
    fun getData(): Call<Data>
    //动态声明(动态添加请求头)
    @GET("get_data.json")
    fun getData(@Header("User-Agent") userAgent: String, @Header("Cache-Control") cacheControl: String): Call<Data>
}

2.5 发起网络请求

//创建网络请求接口的实例
val personService = retrofit.create(PersonService::class.java)
//获取Call对象(对发送请求进行封装)
val personCall: Call<list<Person>> = personService.getPerson()
//发送网络请求(异步是.enqueue(),同步是.excute())
personCall.enqueue(object : Callback<List<person>> {    //接口回调
    override fun onResponse(call: Call<List<person>>, response: Response<List<person>>) {
        //对response做判断
        val list = response.body()    //得到解析后的对象
    }
    override fun onFailure(call: Call<List<person>>, t: Trouble) {
        t.printStackTrace()
    }
})

2.6 Android写法

2.6.1 Retrofit客户端

object RetrofitClient {
    //Cookie
    private val cookieJar by lazy {
        PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(APP.context))
    }
    //OkHttpClient
    private val okHeepClient by lazy {
        OkHttpClient.Builder()
            .cookieJar(cookieJar)
            .build()
    }
    //Retrofit
    private val retrofit by lazy {
        Retrofit.Builder()
            .client(okHeepClient)
            .baseUrl(ApiService.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    //ApiService
    val apiService: ApiService by lazy {
        retrofit.create(ApiService::class.java)
    }
}

2.6.2 返回数据的基类

//返回数据的基类
data class ApiResponse<T>(
    val errorCode: Int,
    val errorMsg: String,
    val data: T?
) {
    fun getResult(): Result<T> {    //使用Result类包装返回结果(数据或异常)
        return if (errorCode == 0 && data != null) {
            Result.success(data)
        } else {
            Result.failure(ApiException(errorMsg))
        }
    }
}

//异常封装
class ApiException(
    errorMsg: String
) : Exception(errorMsg)

2.6.3 API接口

在接口方法前添加suspend关键字,返回值直接为数据模型(无需Call<T>)。

interface ApiService {
    companion object {
        const val BASE_URL = "https://www.wanandroid.com/"
    }
    //登录
    @FormUrlEncoded
    @POST("user/login")
    suspend fun login(
        @Field("username") userName: String,
        @Field("password") password: String
    ): ApiResponse<LoginBean>
}

2.6.4 协程使用

//数据源
//为了“静态”调用,该类无需其他功能就定义成 object 类,有的话使用伴生对象写联网方法
object LoginRemoteDataResource {
    suspend fun login(userName: String, password: String) =
        RetrofitClient.apiService.login(userName, password).getResult()
}

//仓库
interface IRepository {
    suspend fun login(userName: String, password: String): Result<LoginBean>
}
class Repository : IRepository {
    override suspend fun login(userName: String, password: String) =
        LoginRemoteDataResource.login(userName, password)
}

//ViewModel
class SplashViewModel : ViewModel() {
    private val repository: IRepository = Repository()
    private val _loginData = MutableLiveData<Result<LoginBean>>()
    val loginData =  _loginData as LiveData<Result<LoginBean>> //幕后属性,提供不可变版本供外部访问

    suspend fun login(userName: String, password: String){
        val result = repository.login(userName, password)
        _loginData.value = result
    }
}

//UI
viewModel.loginData.observe(this) { it ->    //对Result包装的返回结果是数据还是异常做判断
        it.onSuccess {
            activity.switchFragment(R.id.action_splashLoginFragment_to_mainActivity)
            activity.finish()
        }.onFailure {
            showToast(it.message.orEmpty())
        }
    }
}

三、异常处理

封装工具类(如ErrorHandler),集中处理不同类型的错误,避免重复代码。  根据错误类型向用户展示提示(如网络异常提示“检查网络连接”,401提示“登录已过期”)。  在Release版本中仅记录关键错误信息,避免敏感数据泄露,Debug版本可打印详细日志便于调试。    

错误类型原因异常类型、处理方式
网络异常设备无网络、网络信号差、服务器不可达、请求超时、连接失败。IOException(如SocketTimeoutException、UnknownHostException)
HTTP错误码Response.isSuccessful()返回false(响应码不在200-299范围内,如请求参数错误(400)、未授权(401)、资源不存在(404)、服务器内部错误(500)等。通过Response.code()获取错误码,Response.errorBody()获取错误信息(需转换为字符串)。HttpException。
数据解析异常服务器返回的JSON与本地数据模型字段不匹配(如字段缺失、类型错误)。在Call.enqueue()的onFailure()或协程的catch块中捕获。JsonParseException、MoshiJsonException。
runCatching {
    RetrofitClient.apiService.getData("")
}.onFailure { throwable ->  
    when(throwable) {
        is IOException -> {
            //网络异常
        }
        is JsonParseException ->{
            //解析异常
        }
        is HttpException -> {
            //http异常(4xx/5xx)
            val errorCode = throwable.code()
            val errorMsg = throwable.response()?.errorBody()?.toString() ?: "未知错误"
        }
    }
}

四、解决 http 链接无法访问

Android 9开始默认只允许使用 HTTPS 类型的网络请求,HTTP明文传输因为有安全隐患不再支持。net:ERR_CLEARTEXT_NOT_PERMITTED

3.1 方式一

直接在 AndroidManifest 的 <application> 中添加如下代码:

android:usesCleartextTraffic="true"

3.2 方式二

右键res目录→New→Directory→创建一个xml目录,右键xml目录→New→File→创建一个network_security_config.xml文件,修改内容如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">www.baidu.com</domain>
    </domain-config>
</network-security-config>
Manifest {
    //添加网络访问权限
    <uses-permission android:name="android.permission.INTERNET" />
    //允许HTTP访问
    <application
        android:networkSecurityConfig="@xml/network_security_config"
    </application>
}
<think>我们被要求介绍Android平台上Retrofit库及其使用方法。根据引用资料,Retrofit是Square公司开发的一个类型安全的HTTP客户端,它基于OkHttp,并提供了更简洁的API和扩展性,特别适合在Android和Java开发中使用。Retrofit通过注解和接口封装,使得网络请求更加面向对象。 我们将按照以下结构进行介绍: 1. Retrofit简介 2. 添加依赖 3. 创建数据模型 4. 定义API接口 5. 创建Retrofit实例并发送请求 6. Retrofit与Kotlin协程的结合(特别是2.6.0版本后的改进) 注意:在回答中,所有行内数学表达式(如果有的话)使用$...$,独立公式使用$$...$$(但本主题可能较少涉及数学公式,所以可能不出现)。同时,在引用内容后添加引用标识。 根据引用[2]:Retrofit 是 Square 公司开发的一款类型安全的 HTTP 客户端,它在 Android 和 Java 开发领域被广泛应用。Retrofit 以其简洁的 API 设计和强大的扩展性,极大地简化了网络请求的编写过程,让开发者能够专注于业务逻辑的实现。[^2] 根据引用[3]:Retrofit网络库是在OkHttp的基础上增加了注解、接口等方式进一步开发出来的应用层网络通信库,而OkHttp侧重的是底层通信的实现(Retrofit侧重的是上层接口的封装)[^3]。 下面开始详细介绍: </think> ### Android平台上Retrofit库介绍及使用方法 #### 一、Retrofit库简介 Retrofit是Square公司开发的**类型安全HTTP客户端**,基于OkHttp进行二次封装,通过注解和接口抽象简化网络请求流程。其核心优势包括: 1. **类型安全**:自动将HTTP API转换为Java/Kotlin接口,减少手动解析错误 2. **注解驱动**:使用简洁注解(如`@GET`, `@POST`)定义请求参数 3. **数据转换**:支持JSON/XML等格式自动序列化(通过Gson等转换器) 4. **异步支持**:原生兼容RxJava、Kotlin协程等异步方案[^2][^3] > 引用[3]明确指出:*Retrofit是在OkHttp基础上增加了注解、接口封装的网络通信库,侧重面向对象设计*[^3]。 #### 二、配置步骤(以Kotlin为例) **1. 添加依赖** ```gradle dependencies { // Retrofit核心库 implementation 'com.squareup.retrofit2:retrofit:2.9.0' // Gson转换器(JSON解析) implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // 协程支持(可选) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' } ``` > 注意:Retrofit 2.6.0+ 已内置协程支持[^1][^5] **2. 创建数据模型** ```kotlin data class User( val id: Int, val name: String, val email: String ) ``` **3. 定义API接口** ```kotlin interface ApiService { // GET请求示例 @GET("users/{id}") suspend fun getUser(@Path("id") userId: Int): User // 协程挂起函数 // POST请求示例 @POST("users/create") suspend fun createUser(@Body user: User): Response<Unit> } ``` **4. 初始化Retrofit** ```kotlin val retrofit = Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() val apiService = retrofit.create(ApiService::class.java) ``` **5. 发起网络请求(协程方式)** ```kotlin viewModelScope.launch { try { val user = apiService.getUser(123) Log.d("Retrofit", "用户数据: ${user.name}") } catch (e: Exception) { Log.e("Retrofit", "请求失败", e) } } ``` #### 三、关键特性对比 | 版本 | 异步方案 | 代码复杂度 | 协程支持 | |--------------|---------------|------------|-------------------| | Retrofit <2.6| Callback/RxJava| 较高 | 需额外配置 | | Retrofit ≥2.6| 内置协程 | **极简** | 直接使用`suspend`函数[^1] | > 引用[1]强调:*Retrofit 2.6.0+的协程支持使网络请求代码量减少40%以上*[^1] #### 四、最佳实践 1. **统一错误处理**:通过`Response`类检查HTTP状态码 ```kotlin if (response.isSuccessful) { // 处理数据 } else { // 解析错误信息 } ``` 2. **添加拦截器**:使用OkHttp Interceptor实现日志/认证 3. **超时配置**:在OkHttpClient中设置连接/读写超时 ```kotlin val okHttpClient = OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .build() ``` #### 五、常见问题解决 - **JSON解析失败**:检查数据模型字段是否与JSON键名匹配 - **网络权限缺失**:确保AndroidManifest.xml已添加: ```xml <uses-permission android:name="android.permission.INTERNET" /> ``` - **PROGUARD混淆**:添加规则保留数据模型类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值