1. 现代Android网络请求的基石组合
OkHttp和Retrofit这对黄金搭档已经成为Android开发中处理网络请求的事实标准。作为Square公司开源的明星项目,OkHttp提供了高效的HTTP客户端实现,而Retrofit则在其基础上通过注解和接口抽象,让网络请求变得异常简洁。这种组合不仅大幅减少了样板代码,还通过统一的拦截器机制实现了请求/响应的全流程控制。
在实际项目中,我们经常需要处理多种数据格式的请求与响应。JSON作为主流数据交换格式自然不必多说,但在某些传统企业系统中XML仍占主导地位,而Protobuf则因其高效的二进制编码在性能敏感场景中备受青睐。表单数据则是Web交互中的常客,文件上传等场景离不开它。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础环境搭建与配置
2.1 依赖引入
首先在build.gradle中添加必要依赖:
groovy复制dependencies {
// OkHttp核心库
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
// Retrofit核心库
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// JSON转换器
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// XML转换器
implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'
// Protobuf转换器
implementation 'com.squareup.retrofit2:converter-protobuf:2.9.0'
// 日志拦截器(调试用)
implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0'
}
注意:SimpleXML转换器在处理复杂XML结构时可能不够灵活,对于复杂XML场景建议使用JAXB或自定义解析器。
2.2 Retrofit实例配置
创建Retrofit实例时需要根据不同的数据格式配置对应的转换器:
kotlin复制val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create()) // JSON支持
.addConverterFactory(SimpleXmlConverterFactory.create()) // XML支持
.addConverterFactory(ProtoConverterFactory.create()) // Protobuf支持
.addConverterFactory(FormConverterFactory()) // 自定义表单转换器
.build()
这里我们添加了四种转换器工厂,其中FormConverterFactory需要自定义实现,后文会详细介绍。
3. JSON数据处理实战
3.1 基础JSON请求
定义API接口:
kotlin复制interface ApiService {
@GET("user/{id}")
suspend fun getUser(@Path("id") userId: String): User
@POST("user/create")
@Headers("Content-Type: application/json")
suspend fun createUser(@Body user: User): Response<CreateUserResponse>
}
对应的数据类:
kotlin复制data class User(
@SerializedName("user_id")
val id: String,
val name: String,
val email: String,
@SerializedName("created_at")
val createTime: Long
)
data class CreateUserResponse(
val success: Boolean,
val message: String
)
3.2 高级JSON特性
3.2.1 自定义Gson配置
可以通过自定义Gson实例来处理更复杂的场景:
kotlin复制val gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
// 在Retrofit构建时使用
.addConverterFactory(GsonConverterFactory.create(gson))
3.2.2 处理动态JSON字段
当接口返回的JSON结构不确定时,可以使用JsonElement或Map:
kotlin复制@GET("dynamic-data")
suspend fun getDynamicData(): Response<JsonElement>
// 或者
@GET("dynamic-data")
suspend fun getDynamicData(): Response<Map<String, Any>>
4. 表单数据处理方案
4.1 标准表单提交
对于application/x-www-form-urlencoded格式的表单:
kotlin复制@FormUrlEncoded
@POST("login")
suspend fun login(
@Field("username") username: String,
@Field("password") password: String
): Response<LoginResponse>
4.2 多部分表单(文件上传)
kotlin复制@Multipart
@POST("upload")
suspend fun uploadFile(
@Part("description") description: RequestBody,
@Part file: MultipartBody.Part
): Response<UploadResponse>
// 调用示例
val file = File("/path/to/file.jpg")
val requestFile = file.asRequestBody("image/jpeg".toMediaType())
val part = MultipartBody.Part.createFormData("file", file.name, requestFile)
apiService.uploadFile(
"文件描述".toRequestBody(MultipartBody.FORM),
part
)
4.3 自定义表单转换器
Retrofit默认不提供纯表单的转换器,我们可以自己实现:
kotlin复制class FormConverterFactory : Converter.Factory() {
override fun requestBodyConverter(
type: Type,
parameterAnnotations: Array<Annotation>,
methodAnnotations: Array<Annotation>,
retrofit: Retrofit
): Converter<*, RequestBody>? {
if (parameterAnnotations.any { it is Field }) {
return FormUrlEncodedConverter
}
return null
}
object FormUrlEncodedConverter : Converter<Map<String, String>, RequestBody> {
override fun convert(value: Map<String, String>): RequestBody {
val formBody = FormBody.Builder()
value.forEach { (key, value) ->
formBody.add(key, value)
}
return formBody.build()
}
}
}
5. XML数据处理方案
5.1 简单XML解析
首先定义XML对应的数据类:
kotlin复制@Root(name = "user", strict = false)
data class XmlUser @JvmOverloads constructor(
@field:Element(name = "id")
var id: String = "",
@field:Element(name = "name")
var name: String = "",
@field:Element(name = "email")
var email: String = ""
)
API接口定义:
kotlin复制@GET("user/xml/{id}")
@Headers("Accept: application/xml")
suspend fun getUserXml(@Path("id") userId: String): XmlUser
5.2 处理复杂XML结构
对于包含命名空间、CDATA等复杂结构的XML,SimpleXML可能不够用。这时可以考虑:
- 使用JAXBConverterFactory(需要额外依赖)
- 直接解析原始XML字符串:
kotlin复制@GET("complex/xml")
@Headers("Accept: application/xml")
suspend fun getComplexXml(): Response<ResponseBody>
// 然后手动解析
val xmlString = response.body()?.string()
val document = DocumentBuilderFactory
.newInstance()
.newDocumentBuilder()
.parse(InputSource(StringReader(xmlString)))
6. Protobuf高效传输方案
6.1 Protobuf配置
首先定义.proto文件:
proto复制syntax = "proto3";
message User {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
message UserResponse {
repeated User users = 1;
}
编译生成Java类后,可以直接在Retrofit中使用:
kotlin复制@GET("users/protobuf")
suspend fun getUsersProtobuf(): UserResponse
6.2 Protobuf性能优化
Protobuf的优势在于其高效的二进制编码,但要注意:
- 启用gzip压缩进一步提升传输效率
- 对于大型数据集,考虑使用分块传输
- 在Android上注意protobuf生成类的方法数影响
kotlin复制val okHttpClient = OkHttpClient.Builder()
.addInterceptor(GzipRequestInterceptor()) // 自定义gzip请求拦截器
.build()
7. 多格式动态处理策略
7.1 内容协商(Content Negotiation)
服务器可能根据Accept头返回不同格式的数据:
kotlin复制@GET("resource/{id}")
suspend fun getResource(
@Path("id") id: String,
@Header("Accept") accept: String = "application/json"
): Response<ResponseBody>
7.2 统一响应封装
对于返回格式固定的API,可以统一封装:
kotlin复制data class ApiResponse<T>(
val code: Int,
val message: String,
val data: T
)
// 使用Gson的TypeToken处理泛型
val type = object : TypeToken<ApiResponse<User>>() {}.type
val response = gson.fromJson<ApiResponse<User>>(jsonString, type)
7.3 动态转换器选择
根据URL后缀自动选择转换器:
kotlin复制val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(
ConverterFactoryWrapper(
GsonConverterFactory.create(),
SimpleXmlConverterFactory.create(),
ProtoConverterFactory.create()
)
)
.build()
class ConverterFactoryWrapper(
private val jsonFactory: Converter.Factory,
private val xmlFactory: Converter.Factory,
private val protoFactory: Converter.Factory
) : Converter.Factory() {
override fun responseBodyConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
val contentType = annotations
.filterIsInstance<Headers>()
.flatMap { it.value.toList() }
.firstOrNull { it.startsWith("Accept:") }
?.substringAfter("Accept:")
?.trim()
return when {
contentType?.contains("xml") == true -> xmlFactory.responseBodyConverter(type, annotations, retrofit)
contentType?.contains("protobuf") == true -> protoFactory.responseBodyConverter(type, annotations, retrofit)
else -> jsonFactory.responseBodyConverter(type, annotations, retrofit)
}
}
}
8. 实战经验与性能优化
8.1 拦截器的高级应用
8.1.1 公共参数注入
kotlin复制class CommonParamsInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val url = originalRequest.url.newBuilder()
.addQueryParameter("device_id", getDeviceId())
.addQueryParameter("app_version", BuildConfig.VERSION_NAME)
.build()
val request = originalRequest.newBuilder()
.url(url)
.addHeader("Authorization", "Bearer $token")
.build()
return chain.proceed(request)
}
}
8.1.2 请求重试机制
kotlin复制class RetryInterceptor(private val maxRetries: Int) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
var response = chain.proceed(request)
var retryCount = 0
while (!response.isSuccessful && retryCount < maxRetries) {
retryCount++
response.close()
response = chain.proceed(request)
}
return response
}
}
8.2 连接池优化
kotlin复制val connectionPool = ConnectionPool(
maxIdleConnections = 5, // 最大空闲连接数
keepAliveDuration = 5, // 保持时间(分钟)
timeUnit = TimeUnit.MINUTES
)
val okHttpClient = OkHttpClient.Builder()
.connectionPool(connectionPool)
.build()
8.3 缓存策略
kotlin复制val cacheSize = 10 * 1024 * 1024 // 10MB
val cache = Cache(File(context.cacheDir, "http_cache"), cacheSize.toLong())
val okHttpClient = OkHttpClient.Builder()
.cache(cache)
.addInterceptor(CacheInterceptor())
.addNetworkInterceptor(OnlineCacheInterceptor())
.build()
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if (!NetworkUtil.isNetworkAvailable()) {
request = request.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=${60 * 60 * 24}")
.build()
}
return chain.proceed(request)
}
}
class OnlineCacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
return response.newBuilder()
.header("Cache-Control", "public, max-age=60")
.build()
}
}
8.4 数据压缩
kotlin复制val okHttpClient = OkHttpClient.Builder()
.addInterceptor(BrotliInterceptor()) // 支持Brotli压缩
.build()
class BrotliInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val compressedRequest = originalRequest.newBuilder()
.header("Accept-Encoding", "br")
.build()
return chain.proceed(compressedRequest)
}
}
9. 常见问题排查
9.1 JSON解析异常处理
kotlin复制try {
val response = apiService.getUser("123")
} catch (e: JsonSyntaxException) {
// 处理JSON格式错误
Log.e("API", "JSON解析错误", e)
} catch (e: IOException) {
// 处理网络错误
Log.e("API", "网络错误", e)
} catch (e: HttpException) {
// 处理HTTP错误码
val errorBody = e.response()?.errorBody()?.string()
Log.e("API", "HTTP错误: $errorBody")
}
9.2 XML命名空间问题
SimpleXML处理命名空间时需要特别配置:
kotlin复制@Root(name = "user", strict = false)
@NamespaceList(
Namespace(reference = "http://example.com/ns", prefix = "ex")
)
data class XmlUserWithNamespace(
@field:Element(name = "id")
@field:Namespace(prefix = "ex")
var id: String = ""
)
9.3 Protobuf字段兼容性
当后端修改了.proto文件但客户端未更新时,可以设置:
kotlin复制ProtoConverterFactory.create().withIgnoreUnknownFields(true)
9.4 表单编码问题
处理非ASCII字符的表单数据时:
kotlin复制@FormUrlEncoded
@POST("search")
suspend fun search(
@Field("keyword", encoded = true) keyword: String
): Response<SearchResponse>
10. 测试策略与Mock方案
10.1 单元测试
使用MockWebServer进行API测试:
kotlin复制val mockWebServer = MockWebServer()
@Before
fun setup() {
mockWebServer.start()
val retrofit = Retrofit.Builder()
.baseUrl(mockWebServer.url("/"))
.addConverterFactory(GsonConverterFactory.create())
.build()
apiService = retrofit.create(ApiService::class.java)
}
@Test
fun testGetUser() {
mockWebServer.enqueue(
MockResponse()
.setBody("""{"id":"123","name":"Test User"}""")
.setResponseCode(200)
)
val response = apiService.getUser("123").execute()
assertTrue(response.isSuccessful)
assertEquals("123", response.body()?.id)
}
@After
fun tearDown() {
mockWebServer.shutdown()
}
10.2 接口契约测试
使用Pact进行消费者驱动的契约测试:
kotlin复制@Pact(consumer = "AndroidApp", provider = "UserService")
fun getUserPact(builder: PactDslWithProvider): RequestResponsePact {
return builder
.given("user 123 exists")
.uponReceiving("a request for user 123")
.path("/user/123")
.method("GET")
.willRespondWith()
.status(200)
.body(
PactDslJsonBody()
.stringType("id", "123")
.stringType("name", "Test User")
)
.toPact()
}
@PactVerification(fragment = "getUserPact")
@Test
fun testGetUserContract() {
val response = apiService.getUser("123").execute()
assertTrue(response.isSuccessful)
}
10.3 性能测试
使用OkHttp的EventListener监控请求性能:
kotlin复制val okHttpClient = OkHttpClient.Builder()
.eventListener(object : EventListener() {
override fun callStart(call: Call) {
super.callStart(call)
// 记录开始时间
}
override fun callEnd(call: Call) {
super.callEnd(call)
// 计算耗时
}
})
.build()
11. 架构设计建议
11.1 分层架构
推荐采用清晰的分层架构:
code复制UI层 -> ViewModel层 -> Repository层 -> DataSource层(Remote/Local)
其中Retrofit服务位于DataSource层:
kotlin复制class UserRemoteDataSource(
private val apiService: ApiService
) {
suspend fun getUser(id: String): User {
return apiService.getUser(id)
}
}
11.2 依赖注入
使用Hilt或Koin管理Retrofit实例:
kotlin复制// Hilt模块
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor())
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
11.3 响应式扩展
为Retrofit接口添加Flow支持:
kotlin复制suspend fun <T> safeApiCall(call: suspend () -> T): Result<T> {
return try {
Result.success(call())
} catch (e: Exception) {
Result.failure(e)
}
}
fun <T> apiCallFlow(call: suspend () -> T): Flow<Result<T>> {
return flow {
emit(safeApiCall(call))
}.flowOn(Dispatchers.IO)
}
// 使用示例
val userFlow = apiCallFlow { apiService.getUser("123") }
.map { result ->
result.fold(
onSuccess = { it },
onFailure = { throw it }
)
}
12. 未来演进方向
12.1 支持gRPC
Retrofit可以扩展支持gRPC:
kotlin复制val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(ProtoConverterFactory.create())
.addCallAdapterFactory(GrpcCallAdapterFactory.create())
.build()
12.2 多平台支持
通过Kotlin Multiplatform共享网络层代码:
kotlin复制// commonMain
expect fun createHttpClient(): HttpClient
// androidMain
actual fun createHttpClient(): HttpClient {
return OkHttpClient()
}
// iosMain
actual fun createHttpClient(): HttpClient {
return NSURLSessionClient()
}
12.3 性能监控集成
集成APM工具监控网络性能:
kotlin复制class MonitoringInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.nanoTime()
val response = try {
chain.proceed(request)
} catch (e: Exception) {
// 上报错误
reportError(e)
throw e
}
val duration = (System.nanoTime() - startTime) / 1_000_000
reportMetric(request.url.toString(), duration, response.code)
return response
}
}
