前言
在现代 Android 开发中,网络请求与 JSON 数据处理是密不可分的。OkHttp 作为强大的 HTTP 客户端,与 JSON 解析库(Moshi/Jackson/Gson)的结合使用,可以极大简化网络请求与数据解析的流程。本文将详细介绍如何将 OkHttp 与这三种主流 JSON 库结合使用,帮助开发者选择最适合自己项目的技术方案。
一、三种 JSON 库概览
1. Gson
优点:Google 官方支持,API 简单易用,社区资源丰富
缺点:性能相对较低,缺乏对 Kotlin 的深度支持
2. Jackson
优点:性能优异,功能全面,支持多种数据格式
缺点:API 较复杂,初始配置繁琐
3. Moshi
优点:专为 Kotlin 设计,轻量高效,与 OkHttp 同属 Square 产品线
缺点:社区相对较小,功能不如 Jackson 全面
二、基础集成与配置
1. 添加依赖
Gradle 配置:
// OkHttp 核心库
implementation 'com.squareup.okhttp3:okhttp:4.10.0'// 各 JSON 库选择其一即可
implementation 'com.squareup.moshi:moshi:1.14.0' // Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.14.0' // Moshi Kotlin 支持
kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.14.0' // Moshi 代码生成implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.1' // Jacksonimplementation 'com.google.code.gson:gson:2.10' // Gson
三、OkHttp 与 Moshi 结合使用
1. 基本配置
// 创建 Moshi 实例
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()) // 支持 Kotlin 类.build()// 创建 OkHttpClient
val okHttpClient = OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).build()// 使用示例
suspend fun fetchUser(): User {val request = Request.Builder().url("https://api.example.com/user/1").build()okHttpClient.newCall(request).execute().use { response ->if (!response.isSuccessful) throw IOException("Unexpected code $response")val moshiAdapter = moshi.adapter(User::class.java)return moshiAdapter.fromJson(response.body?.source())!!}
}
2. 配合 Retrofit 使用
val retrofit = Retrofit.Builder().baseUrl("https://api.example.com/").client(okHttpClient).addConverterFactory(MoshiConverterFactory.create(moshi)).build()interface ApiService {@GET("user/{id}")suspend fun getUser(@Path("id") userId: String): User
}
3. 自定义适配器(处理特殊格式)
class DateAdapter {@ToJsonfun toJson(date: Date): String {return SimpleDateFormat("yyyy-MM-dd").format(date)}@FromJsonfun fromJson(dateString: String): Date {return SimpleDateFormat("yyyy-MM-dd").parse(dateString)!!}
}// 使用自定义适配器
val moshi = Moshi.Builder().add(DateAdapter()).add(KotlinJsonAdapterFactory()).build()
四、OkHttp 与 Jackson 结合使用
1. 基本配置
// 创建 ObjectMapper
val objectMapper = ObjectMapper().registerModule(KotlinModule())// 使用示例
fun fetchUser(): User {val request = Request.Builder().url("https://api.example.com/user/1").build()okHttpClient.newCall(request).execute().use { response ->if (!response.isSuccessful) throw IOException("Unexpected code $response")return objectMapper.readValue(response.body?.string(), User::class.java)}
}
2. 配合 Retrofit 使用
val retrofit = Retrofit.Builder().baseUrl("https://api.example.com/").client(okHttpClient).addConverterFactory(JacksonConverterFactory.create(objectMapper)).build()
3. 处理复杂情况
// 配置 ObjectMapper 处理多种情况
val objectMapper = ObjectMapper().apply {registerModule(KotlinModule.Builder().build())configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
}
五、OkHttp 与 Gson 结合使用
1. 基本配置
// 创建 Gson 实例
val gson = GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create()// 使用示例
fun fetchUser(): User {val request = Request.Builder().url("https://api.example.com/user/1").build()okHttpClient.newCall(request).execute().use { response ->if (!response.isSuccessful) throw IOException("Unexpected code $response")return gson.fromJson(response.body?.charStream(), User::class.java)}
}
2. 配合 Retrofit 使用
val retrofit = Retrofit.Builder().baseUrl("https://api.example.com/").client(okHttpClient).addConverterFactory(GsonConverterFactory.create(gson)).build()
3. 自定义类型适配器
val gson = GsonBuilder().registerTypeAdapter(Date::class.java, object : JsonDeserializer<Date> {override fun deserialize(json: JsonElement,typeOfT: Type,context: JsonDeserializationContext): Date {return SimpleDateFormat("yyyy-MM-dd").parse(json.asString)!!}}).create()
六、三种方案对比与选型建议
1. 性能对比
Jackson:解析速度最快,内存占用最低
Moshi:性能接近 Jackson,略优于 Gson
Gson:性能相对较低,但在大多数场景足够
2. 功能对比
特性 | Moshi | Jackson | Gson |
---|---|---|---|
Kotlin 支持 | ✓✓ | ✓ | ✓ |
代码生成 | ✓ | ✓ | ✗ |
灵活性 | ✓✓ | ✓✓✓ | ✓✓ |
学习曲线 | 低 | 中 | 低 |
3. 选型建议
新项目(Kotlin):优先选择 Moshi,特别是与 OkHttp/Retrofit 配套使用
大型复杂项目:考虑 Jackson,特别是需要处理复杂 JSON 结构时
维护旧项目:继续使用 Gson,特别是项目已经大量依赖 Gson 时
七、高级技巧与最佳实践
1. 统一错误处理
inline fun <reified T> OkHttpClient.parseResponse(request: Request): T {this.newCall(request).execute().use { response ->val body = response.body?.string() ?: throw IOException("Empty response")if (!response.isSuccessful) {// 尝试解析错误信息val errorResponse = moshi.adapter(ErrorResponse::class.java).fromJson(body)throw ApiException(code = response.code,message = errorResponse?.message ?: "Unknown error")}return moshi.adapter(T::class.java).fromJson(body)!!}
}
2. 缓存网络响应
inline fun <reified T> OkHttpClient.parseResponse(request: Request): T {this.newCall(request).execute().use { response ->val body = response.body?.string() ?: throw IOException("Empty response")if (!response.isSuccessful) {// 尝试解析错误信息val errorResponse = moshi.adapter(ErrorResponse::class.java).fromJson(body)throw ApiException(code = response.code,message = errorResponse?.message ?: "Unknown error")}return moshi.adapter(T::class.java).fromJson(body)!!}
}
3. 日志拦截器
class JsonPrettyPrintInterceptor : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()val response = chain.proceed(request)val content = response.body?.string() ?: return responsetry {val prettyJson = when {content.startsWith("{") -> JsonParser.parseString(content).toString()content.startsWith("[") -> JsonParser.parseString(content).toString()else -> content}Log.d("Network", prettyJson)} catch (e: Exception) {Log.d("Network", content)}return response.newBuilder().body(content.toResponseBody(response.body?.contentType())).build()}
}
八、常见问题解决方案
1. 处理 JSON 字段与 Kotlin 属性名不一致
Moshi:
@Json(name = "user_name")
val userName: String
Jackson:
@JsonProperty("user_name")
val userName: String
Gson:
@SerializedName("user_name")
val userName: String
2. 处理 null 值
Moshi:
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).add(NullToEmptyStringAdapter()).build()class NullToEmptyStringAdapter {@FromJson fun fromJson(reader: JsonReader): String {return if (reader.peek() == JsonReader.Token.NULL) {reader.nextNull()""} else {reader.nextString()}}
}
3. 处理多态类型
Jackson:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,include = JsonTypeInfo.As.PROPERTY,property = "type"
)
@JsonSubTypes(JsonSubTypes.Type(value = Dog::class, name = "dog"),JsonSubTypes.Type(value = Cat::class, name = "cat")
)
abstract class Animal
九、总结
OkHttp 与 JSON 解析库的结合为 Android 开发提供了强大的网络数据处理能力。通过本文的介绍,我们可以看到:
Moshi 是与 Kotlin 最契合的选择,特别适合新项目开发
Jackson 在性能和处理复杂场景方面表现优异
Gson 仍然是不错的选择,特别是对已有项目的维护
无论选择哪种方案,都应考虑项目的具体需求、团队熟悉度和长期维护成本。正确配置和使用这些工具可以显著提高开发效率和应用程序性能。
在实际开发中,建议:
新项目优先考虑 Moshi
性能敏感型项目考虑 Jackson
保持项目的一致性,避免混用多种 JSON 库
合理利用各种高级特性处理复杂场景