本项目实现了一套完整的Android网络请求框架,基于OkHttp + Retrofit + 协程 + Gson的黄金组合,提供了统一的响应格式、完善的异常处理、自动日志记录、Token管理和智能缓存等功能。
博主在之前的毕设项目搭建过程中使用过这个Okhttp+Retrofit这一套,可以说非常优雅,扩展性也比较好。因为工作中接触应用层比较少了,今天一时兴起想起写一篇文章,万一哪天被裁了,也好向上兼容找一个应用开发的岗位,哈哈哈...
1.项目依赖版本
- **OkHttp 4.12.0** - 底层HTTP客户端
- **Retrofit 2.9.0** - RESTful API封装
- **Gson 2.10.1** - JSON序列化/反序列化
- **Kotlin Coroutines 1.7.3** - 异步编程支持
- **Lifecycle Runtime KTX 2.7.0** - 生命周期感知的协程支持
dependencies {
// OkHttp
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// Gson
implementation("com.google.code.gson:gson:2.10.1")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
// Lifecycle
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
2.项目结构
com.zzzjian.networkdemo
├── MainActivity.kt # 主Activity,使用示例
└── network
├── BaseResponse.kt # 统一响应格式
├── Result.kt # 请求结果封装
├── RetrofitClient.kt # Retrofit客户端单例
├── ApiService.kt # API接口定义
├── RequestExtensions.kt # 请求扩展函数和异常处理
└── interceptor
├── LogInterceptor.kt # 日志拦截器
├── TokenInterceptor.kt # Token拦截器
└── CacheInterceptor.kt # 缓存拦截器
3.组件作用
1. BaseResponse.kt - 统一响应格式
- 作用:定义服务器返回数据的统一格式,所有API接口的响应都遵循这个结构。
- 在网络链路中的位置:作为Retrofit接口方法的返回类型,接收服务器返回的原始数据。
data class BaseResponse<T>(
@SerializedName("code")
val code: Int = 0,
@SerializedName("message")
val message: String = "",
@SerializedName("data")
val data: T? = null
) {
val isSuccess: Boolean
get() = code == 0
}
2.Result.kt - 请求结果封装
- 作用:使用密封类封装网络请求的三种状态(成功、失败、加载中),提供类型安全的处理方式。
- 在网络链路中的位置:作为safeApiCall函数的返回值,将BaseResponse转换为更易处理的Result对象。
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
/**
* 成功状态处理扩展函数
* 作用:当Result为Success状态时执行指定操作
*/
inline fun <T> Result<T>.onSuccess(action: (T) -> Unit): Result<T> {
if (this is Result.Success) action(data)
return this
}
/**
* 失败状态处理扩展函数
* 作用:当Result为Error状态时执行指定操作
*/
inline fun <T> Result<T>.onError(action: (Exception) -> Unit): Result<T> {
if (this is Result.Error) action(exception)
return this
}
/**
* 加载中状态处理扩展函数
* 作用:当Result为Loading状态时执行指定操作
*/
inline fun <T> Result<T>.onLoading(action: () -> Unit): Result<T> {
if (this is Result.Loading) action()
return this
}
3. LogInterceptor.kt - 日志拦截器
- 作用:拦截并打印所有网络请求和响应的详细信息,便于调试和问题排查。
- 在网络链路中的位置:作为OkHttp的拦截器,在请求发送前和响应接收后执行。
class LogInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 打印请求信息
println("OkHttp Request: ${request.method} ${request.url}")
request.headers.forEach { (name, value) ->
println("OkHttp Request Header: $name = $value")
}
val requestBody = request.body
if (requestBody != null) {
val buffer = okio.Buffer()
requestBody.writeTo(buffer)
val charset = Charset.forName("UTF-8")
val requestString = buffer.readString(charset)
println("OkHttp Request Body: $requestString")
}
// 执行请求
val response = chain.proceed(request)
// 打印响应信息
println("OkHttp Response: ${response.code} ${response.message}")
response.headers.forEach { (name, value) ->
println("OkHttp Response Header: $name = $value")
}
val responseBody = response.body
if (responseBody != null) {
val source = responseBody.source()
source.request(Long.MAX_VALUE)
val buffer = source.buffer
val charset = Charset.forName("UTF-8")
val responseString = buffer.clone().readString(charset)
println("OkHttp Response Body: $responseString")
}
return response
}
}
4. TokenInterceptor.kt - Token拦截器
- 作用:自动为所有网络请求添加认证Token,避免在每个接口手动添加。
- 在网络链路中的位置:作为OkHttp的拦截器,在请求发送前自动注入Token到请求头。
// - 统一管理Token,避免重复代码
// - 支持动态设置和清除Token
// - 使用Bearer Token标准认证方式
class TokenInterceptor : Interceptor {
private var token: String? = null
/**
* 设置认证Token
* @param token 用户登录后获取的Token
*/
fun setToken(token: String) {
this.token = token
}
/**
* 清除认证Token
* 用于用户登出或Token失效时
*/
fun clearToken() {
this.token = null
}
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val requestBuilder = originalRequest.newBuilder()
token?.let {
requestBuilder.addHeader("Authorization", "Bearer $it")
}
val request = requestBuilder.build()
return chain.proceed(request)
}
}
5. CacheInterceptor.kt - 缓存拦截器
- 作用:为网络响应添加缓存策略,支持离线访问和减少网络请求。
- 在网络链路中的位置:作为OkHttp的网络拦截器,在响应接收后添加缓存控制头。
class CacheInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val cacheControl = request.cacheControl.toString()
if (cacheControl.isEmpty()) {
response.newBuilder()
.header("Cache-Control", "public, max-age=60")
.build()
}
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", "public, max-age=${TimeUnit.MINUTES.toSeconds(10)}")
.build()
}
}
6. RetrofitClient.kt - Retrofit客户端单例
- 作用:统一管理Retrofit和OkHttp的配置,提供API接口的创建方法。
- 在网络链路中的位置:作为网络请求的入口,负责创建和配置所有网络请求组件。
// - 连接超时:30秒
// - 读取超时:30秒
// - 写入超时:30秒
// - 缓存大小:10MB
// - 自动重试:开启
object RetrofitClient {
private const val BASE_URL = "https://api.example.com/"
private const val CACHE_SIZE = 10 * 1024 * 1024L // 10MB
private val tokenInterceptor = TokenInterceptor()
private val logInterceptor = LogInterceptor()
private val cacheInterceptor = CacheInterceptor()
/**
* OkHttp客户端配置
* 包含超时设置、拦截器链、缓存配置等
*/
private val okHttpClient: OkHttpClient by lazy {
val cacheDir = File(System.getProperty("java.io.tmpdir"), "okhttp_cache")
val cache = Cache(cacheDir, CACHE_SIZE)
OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(tokenInterceptor)
.addInterceptor(logInterceptor)
.addNetworkInterceptor(cacheInterceptor)
.cache(cache)
.retryOnConnectionFailure(true)
.build()
}
/**
* Retrofit实例配置
* 包含BaseUrl、OkHttp客户端、Gson转换器等
*/
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
/**
* 创建API接口实例
* @param clazz API接口的Class对象
* @return API接口的实现实例
*/
fun <T> createApiService(clazz: Class<T>): T {
return retrofit.create(clazz)
}
/**
* 设置认证Token
* @param token 用户登录后获取的Token
*/
fun setToken(token: String) {
tokenInterceptor.setToken(token)
}
/**
* 清除认证Token
* 用于用户登出或Token失效时
*/
fun clearToken() {
tokenInterceptor.clearToken()
}
}
7. ApiService.kt - API接口定义
作用:定义所有网络请求的接口方法,使用注解声明请求方式和参数。
在网络链路中的位置:作为业务层与网络层的桥梁,定义具体的API端点。
interface ApiService {
/**
* 获取用户信息
* @return BaseResponse<UserInfo> 用户信息响应
*/
@GET("user/info")
suspend fun getUserInfo(): BaseResponse<UserInfo>
/**
* 获取用户列表
* @param page 页码,默认为1
* @param size 每页数量,默认为20
* @return BaseResponse<List<UserInfo>> 用户列表响应
*/
@GET("user/list")
suspend fun getUserList(
@Query("page") page: Int = 1,
@Query("size") size: Int = 20
): BaseResponse<List<UserInfo>>
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @return BaseResponse<LoginResponse> 登录响应,包含Token和用户信息
*/
@POST("user/login")
suspend fun login(
@Query("username") username: String,
@Query("password") password: String
): BaseResponse<LoginResponse>
}
/**
* 用户信息数据模型
*/
data class UserInfo(
val id: Long,
val username: String,
val email: String,
val avatar: String?
)
/**
* 登录响应数据模型
*/
data class LoginResponse(
val token: String,
val userInfo: UserInfo
)
8. RequestExtensions.kt - 请求扩展函数和异常处理
- 作用: 提供安全的网络请求封装,统一处理异常和响应转换。
- 在网络链路中的位置:作为业务层调用网络请求的入口,处理所有异常情况。
// - 自动切换到IO线程
// - 统一异常处理
// - 友好的错误提示
// - 将BaseResponse转换为Result
/**
* 安全的API调用函数
* 作用:封装网络请求,自动处理异常和响应转换
* @param apiCall 网络请求的suspend函数
* @return Result<T> 请求结果,包含成功、失败或加载中状态
*/
suspend fun <T> safeApiCall(
apiCall: suspend () -> BaseResponse<T>
): Result<T> {
return withContext(Dispatchers.IO) {
try {
val response = apiCall()
if (response.isSuccess) {
Result.Success(response.data!!)
} else {
Result.Error(ApiException(response.code, response.message))
}
} catch (e: Exception) {
Result.Error(handleException(e))
}
}
}
/**
* 异常处理函数
* 作用:将原始异常转换为更友好的业务异常
* @param e 原始异常
* @return Exception 处理后的异常
*/
fun handleException(e: Exception): Exception {
return when (e) {
is UnknownHostException -> NetworkException("网络连接失败,请检查网络设置")
is SocketTimeoutException -> NetworkException("请求超时,请稍后重试")
is IOException -> NetworkException("网络错误:${e.message}")
is ApiException -> e
else -> UnknownException("未知错误:${e.message}")
}
}
/**
* API业务异常
* 服务器返回的业务错误(如code非0)
*/
class ApiException(val code: Int, override val message: String) : Exception(message)
/**
* 网络异常
* 网络连接、超时等网络相关错误
*/
class NetworkException(override val message: String) : Exception(message)
/**
* 未知异常
* 其他未预期的错误
*/
class UnknownException(override val message: String) : Exception(message)
9. MainActivity.kt - 使用示例
- 作用:演示如何使用网络框架进行API请求。
- 在网络链路中的位置:作为业务层,调用网络框架进行数据请求。
class MainActivity : AppCompatActivity() {
/**
* API服务实例
* 通过RetrofitClient创建,用于调用具体的API接口
*/
private val apiService: ApiService by lazy {
RetrofitClient.createApiService(ApiService::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// 演示完整的网络请求流程
demonstrateCompleteFlow()
}
/**
* 演示完整的网络请求流程
* 包含:登录、设置Token、获取用户信息、获取用户列表、并发请求、错误处理
*/
private fun demonstrateCompleteFlow() {
lifecycleScope.launch {
Log.d("NetworkDemo", "========== 开始演示完整流程 ==========")
// 步骤1: 用户登录
Log.d("NetworkDemo", "\n步骤1: 用户登录")
loginDemo()
// 步骤2: 获取用户信息(带Token)
Log.d("NetworkDemo", "\n步骤2: 获取用户信息")
getUserInfoDemo()
// 步骤3: 获取用户列表
Log.d("NetworkDemo", "\n步骤3: 获取用户列表")
getUserListDemo()
// 步骤4: 并发请求演示
Log.d("NetworkDemo", "\n步骤4: 并发请求演示")
concurrentRequestsDemo()
// 步骤5: 错误处理演示
Log.d("NetworkDemo", "\n步骤5: 错误处理演示")
errorHandlingDemo()
// 步骤6: 清除Token
Log.d("NetworkDemo", "\n步骤6: 清除Token")
clearTokenDemo()
Log.d("NetworkDemo", "========== 流程演示结束 ==========")
}
}
/**
* 步骤1: 用户登录演示
* 展示如何调用登录接口并处理返回的Token
*/
private suspend fun loginDemo() {
val result = safeApiCall {
apiService.login(
username = "testuser",
password = "password123"
)
}
result
.onLoading {
Log.d("NetworkDemo", "登录中...")
}
.onSuccess { loginResponse ->
Log.d("NetworkDemo", "登录成功!")
Log.d("NetworkDemo", "Token: ${loginResponse.token}")
Log.d("NetworkDemo", "用户信息: ${loginResponse.userInfo}")
// 登录成功后设置Token,后续请求会自动携带
RetrofitClient.setToken(loginResponse.token)
Log.d("NetworkDemo", "Token已设置,后续请求将自动携带")
}
.onError { exception ->
Log.e("NetworkDemo", "登录失败: ${exception.message}")
}
}
/**
* 步骤2: 获取用户信息演示
* 展示如何使用Token进行认证请求
*/
private suspend fun getUserInfoDemo() {
val result = safeApiCall {
apiService.getUserInfo()
}
result
.onLoading {
Log.d("NetworkDemo", "获取用户信息中...")
}
.onSuccess { userInfo ->
Log.d("NetworkDemo", "获取用户信息成功!")
Log.d("NetworkDemo", "用户ID: ${userInfo.id}")
Log.d("NetworkDemo", "用户名: ${userInfo.username}")
Log.d("NetworkDemo", "邮箱: ${userInfo.email}")
Log.d("NetworkDemo", "头像: ${userInfo.avatar ?: "无"}")
}
.onError { exception ->
Log.e("NetworkDemo", "获取用户信息失败: ${exception.message}")
}
}
/**
* 步骤3: 获取用户列表演示
* 展示如何处理列表数据和分页参数
*/
private suspend fun getUserListDemo() {
val result = safeApiCall {
apiService.getUserList(page = 1, size = 10)
}
result
.onLoading {
Log.d("NetworkDemo", "获取用户列表中...")
}
.onSuccess { userList ->
Log.d("NetworkDemo", "获取用户列表成功!")
Log.d("NetworkDemo", "用户数量: ${userList.size}")
userList.forEachIndexed { index, user ->
Log.d("NetworkDemo", " ${index + 1}. ${user.username} (${user.email})")
}
}
.onError { exception ->
Log.e("NetworkDemo", "获取用户列表失败: ${exception.message}")
}
}
/**
* 步骤4: 并发请求演示
* 展示如何使用async/await同时发起多个请求
*/
private suspend fun concurrentRequestsDemo() {
Log.d("NetworkDemo", "同时发起多个请求...")
// 使用async并发执行多个请求
val userInfoDeferred = async {
Log.d("NetworkDemo", " 请求1: 获取用户信息")
safeApiCall { apiService.getUserInfo() }
}
val userListDeferred = async {
Log.d("NetworkDemo", " 请求2: 获取用户列表")
safeApiCall { apiService.getUserList(page = 1, size = 5) }
}
// 等待所有请求完成
val userInfoResult = userInfoDeferred.await()
val userListResult = userListDeferred.await()
Log.d("NetworkDemo", "所有请求已完成")
// 处理第一个请求的结果
userInfoResult
.onSuccess { userInfo ->
Log.d("NetworkDemo", "请求1成功: ${userInfo.username}")
}
.onError { exception ->
Log.e("NetworkDemo", "请求1失败: ${exception.message}")
}
// 处理第二个请求的结果
userListResult
.onSuccess { userList ->
Log.d("NetworkDemo", "请求2成功: 获取到${userList.size}个用户")
}
.onError { exception ->
Log.e("NetworkDemo", "请求2失败: ${exception.message}")
}
}
/**
* 步骤5: 错误处理演示
* 展示如何处理各种异常情况
*/
private suspend fun errorHandlingDemo() {
Log.d("NetworkDemo", "演示错误处理...")
// 模拟网络错误(这里只是演示,实际会调用真实API)
val result = safeApiCall {
apiService.getUserInfo()
}
result
.onLoading {
Log.d("NetworkDemo", "请求中...")
}
.onSuccess { userInfo ->
Log.d("NetworkDemo", "请求成功: ${userInfo.username}")
}
.onError { exception ->
Log.e("NetworkDemo", "请求失败")
Log.e("NetworkDemo", "异常类型: ${exception.javaClass.simpleName}")
Log.e("NetworkDemo", "错误信息: ${exception.message}")
// 根据不同的异常类型进行不同的处理
when (exception) {
is com.zzzjian.networkdemo.network.NetworkException -> {
Log.e("NetworkDemo", "网络异常,请检查网络连接")
}
is com.zzzjian.networkdemo.network.ApiException -> {
Log.e("NetworkDemo", "API异常,错误码: ${(exception as com.zzzjian.networkdemo.network.ApiException).code}")
}
else -> {
Log.e("NetworkDemo", "未知异常")
}
}
}
}
/**
* 步骤6: 清除Token演示
* 展示如何清除Token(用于登出场景)
*/
private fun clearTokenDemo() {
Log.d("NetworkDemo", "清除Token...")
RetrofitClient.clearToken()
Log.d("NetworkDemo", "Token已清除,后续请求将不再携带认证信息")
}
}
4.网络请求完整链路
┌─────────────────────────────────────────────────────────────────┐
│ 业务层 (MainActivity) │
│ │
│ lifecycleScope.launch { │
│ val result = safeApiCall { apiService.getUserInfo() } │
│ result.onSuccess { ... }.onError { ... } │
│ } │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 请求扩展层 (RequestExtensions.kt) │
│ │
│ safeApiCall() │
│ ├─ withContext(Dispatchers.IO) // 切换到IO线程 │
│ ├─ try { apiCall() } // 执行API调用 │
│ ├─ 处理BaseResponse // 转换为Result │
│ └─ catch异常并转换 // 统一异常处理 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ API接口层 (ApiService.kt) │
│ │
│ @GET("user/info") │
│ suspend fun getUserInfo(): BaseResponse<UserInfo> │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Retrofit客户端层 (RetrofitClient.kt) │
│ │
│ Retrofit.create(ApiService::class.java) │
│ └─ 动态代理生成API接口实现 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OkHttp拦截器链 (按顺序执行) │
│ │
│ 1. TokenInterceptor // 添加Token到请求头 │
│ 2. LogInterceptor // 打印请求日志 │
│ 3. CacheInterceptor // 添加缓存策略 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OkHttp网络层 │
│ │
│ ├─ 连接池管理 │
│ ├─ 请求发送 │
│ ├─ 响应接收 │
│ └─ 自动重试 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 服务器 │
│ │
│ 处理请求并返回JSON数据 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 响应处理流程 (反向执行) │
│ │
│ 1. CacheInterceptor // 添加缓存控制头 │
│ 2. LogInterceptor // 打印响应日志 │
│ 3. Gson转换器 // JSON → BaseResponse │
│ 4. safeApiCall // BaseResponse → Result │
│ 5. 业务层处理 // Result.onSuccess/onError │
└─────────────────────────────────────────────────────────────────┘
5.详细请求步骤
1. 业务层发起请求
lifecycleScope.launch {
val result = safeApiCall {
apiService.getUserInfo()
}
result.onSuccess { ... }.onError { ... }
}
2. safeApiCall处理
- 切换到IO线程
- 执行API调用
- 捕获异常并转换
- 将BaseResponse转换为Result
3. Retrofit执行
- 通过动态代理调用API接口方法
- 构建HTTP请求
- 调用OkHttp发送请求
4. OkHttp拦截器链(请求阶段)
- TokenInterceptor - 添加Authorization头
- LogInterceptor - 打印请求信息
- CacheInterceptor - 检查缓存(如果有)
5. 网络传输
- OkHttp发送HTTP请求
- 服务器处理并返回响应
6. OkHttp拦截器链(响应阶段)
- CacheInterceptor - 添加缓存控制头
- LogInterceptor - 打印响应信息
7. 响应处理
- Gson将JSON转换为BaseResponse
- safeApiCall将BaseResponse转换为Result
- 业务层通过扩展函数处理Result
6.架构设计原则
1. 单一职责原则
每个类只负责一个功能:
- BaseResponse - 响应格式定义
- Result - 结果状态封装
- 各Interceptor - 独立的拦截功能
2. 开闭原则
- 通过拦截器扩展功能,无需修改核心代码
- 新增API只需在ApiService中添加方法
3. 依赖倒置原则
- 业务层依赖抽象的ApiService接口
- 不直接依赖具体的网络实现
4. 接口隔离原则
- ApiService只定义需要的接口方法
- Result提供简洁的扩展函数接口
ok,复制粘贴完了,这个黄金网络架构组合是AI生成的,我想我到时候被裁了也不会找到应用层的岗位了,AI可以做的比我好,不管是界面还原,还是架构设计...
未来到底是个什么样的时代,AI可以做到哪一步?