Android 网络层最佳实践:Retrofit + OkHttp 封装与实战

📖 目录

  1. 前言

  2. [为什么要封装 Retrofit + OkHttp](#为什么要封装 Retrofit + OkHttp)

  3. 基础封装思路

  4. [支持多 BaseUrl 的设计](#支持多 BaseUrl 的设计)

  5. 统一响应模型与错误处理

  6. [Token 自动刷新机制](#Token 自动刷新机制)

  7. 完整网络层结构

  8. 性能优化与调试建议

  9. 结语


💬 前言

在 Android 项目开发中,网络请求几乎是所有业务模块的基础。而在众多方案中,Retrofit + OkHttp 无疑是最稳定、最高效的组合。

它不仅支持注解式接口定义、自动序列化,还能与协程完美结合,轻松实现异步网络通信。

本文将从基础到进阶,带你实现一套「企业级」网络层封装,让你的代码更优雅、更安全、更可维护。


⚙️ 为什么要封装 Retrofit + OkHttp

在小型项目中,直接使用 Retrofit 足够简单;

但随着业务扩展,你可能会遇到以下痛点:

  • 不同模块需要使用 不同的 BaseUrl

  • 接口响应结构不一致,需要统一解析;

  • Token 失效需要自动刷新;

  • 需要统一的异常处理机制;

  • 想让 ViewModel 调用更简洁。

这些问题如果不统一封装,会导致项目结构混乱、维护困难。

因此,我们需要构建一个「高内聚、低耦合」的网络请求框架。


🧱 基础封装思路

Retrofit 是一个「上层封装」,核心依赖 OkHttp:

层级 功能 说明
OkHttp 网络层 负责请求发送、拦截、缓存、超时
Retrofit 封装层 负责接口注解解析、数据转换
协程 + Result 安全层 负责异常捕获与结果返回

我们将从 OkHttp 开始封装,逐步构建整个网络体系。


🌐 支持多 BaseUrl 的设计

1. 使用场景

在多模块项目中,不同业务接口往往属于不同域名:

模块 BaseUrl
用户中心 https://user.api.example.com/
支付模块 https://pay.api.example.com/
上传模块 https://upload.api.example.com/

Retrofit 默认只能设置一个 baseUrl

因此我们需要通过 拦截器 + Header 标识 来动态替换。


2. BaseUrlInterceptor.kt

Kotlin 复制代码
class BaseUrlInterceptor : Interceptor {

    companion object {
        const val HEADER_BASE_URL = "Base-Url" // 自定义 Header 名
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val baseUrlHeader = request.header(HEADER_BASE_URL)

        // 如果请求头中包含自定义 BaseUrl
        if (!baseUrlHeader.isNullOrEmpty()) {
            val newUrl = request.url.newBuilder()
                .scheme("https")
                .host(baseUrlHeader)
                .build()

            val newRequest = request.newBuilder()
                .url(newUrl)
                .removeHeader(HEADER_BASE_URL) // 使用完后移除
                .build()

            return chain.proceed(newRequest)
        }

        // 默认请求
        return chain.proceed(request)
    }
}

3. RetrofitManager.kt

Kotlin 复制代码
object RetrofitManager {

    private const val DEFAULT_BASE_URL = "https://user.api.example.com/"

    private val okHttpClient by lazy {
        OkHttpClient.Builder()
            .addInterceptor(BaseUrlInterceptor())
            .addInterceptor(TokenInterceptor())
            .addInterceptor(HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.BODY
            })
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .build()
    }

    fun getService(baseUrl: String = DEFAULT_BASE_URL): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

使用示例:

Kotlin 复制代码
val userApi = RetrofitManager.getService().create(UserApi::class.java)
val payApi = RetrofitManager.getService("https://pay.api.example.com/").create(PayApi::class.java)

🧩 统一响应模型与错误处理

1. 定义统一返回结构

Kotlin 复制代码
data class BaseResponse<T>(
    val code: Int,
    val msg: String?,
    val data: T?
) {
    fun isSuccess() = code == 200
}

2. 安全请求封装(协程版)

Kotlin 复制代码
suspend fun <T> safeApiCall(apiCall: suspend () -> BaseResponse<T>): Result<T> {
    return try {
        val response = apiCall()
        if (response.isSuccess()) {
            Result.success(response.data!!)
        } else {
            Result.failure(Exception(response.msg ?: "未知错误"))
        }
    } catch (e: IOException) {
        Result.failure(Exception("网络连接失败"))
    } catch (e: HttpException) {
        Result.failure(Exception("网络错误: ${e.code()}"))
    } catch (e: Exception) {
        Result.failure(e)
    }
}

3. ViewModel 调用示例

Kotlin 复制代码
class LoginViewModel : ViewModel() {

    private val _loginState = MutableLiveData<Result<LoginResponse>>()
    val loginState: LiveData<Result<LoginResponse>> = _loginState

    fun login(username: String, password: String) {
        viewModelScope.launch {
            val result = safeApiCall {
                RetrofitManager.getService().create(UserApi::class.java)
                    .login(username, password)
            }
            _loginState.value = result
        }
    }
}

🔑 Token 自动刷新机制

Kotlin 复制代码
class TokenInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer ${TokenManager.token}")
            .build()

        val response = chain.proceed(request)

        // Token 过期处理
        if (response.code == 401) {
            synchronized(this) {
                val newToken = TokenManager.refreshToken()
                if (newToken != null) {
                    val newRequest = request.newBuilder()
                        .removeHeader("Authorization")
                        .addHeader("Authorization", "Bearer $newToken")
                        .build()
                    return chain.proceed(newRequest)
                }
            }
        }
        return response
    }
}

📦 网络层完整结构

复制代码
network/
├── ApiService.kt          # 接口定义
├── RetrofitManager.kt     # Retrofit 实例
├── BaseUrlInterceptor.kt  # 多域名支持
├── TokenInterceptor.kt    # Token 刷新
├── NetworkHelper.kt       # 安全请求封装
└── model/
    └── BaseResponse.kt    # 统一响应模型

⚡ 性能优化与调试建议

优化项 建议
⏱️ 超时设置 连接 10s,读取 15s,写入 15s
🧠 缓存策略 静态接口可添加本地缓存
🔍 日志管理 Debug 模式启用 BODY,Release 模式关闭
♻️ 连接池复用 默认 Keep-Alive,可减少握手时间
🧩 异常监控 在 Result.failure 中上报错误信息

🏁 结语

通过这套封装,我们构建了一个高扩展性、高可维护性的网络层:

层级 功能 示例
通信层 OkHttp 超时、缓存、拦截器
封装层 Retrofit 动态接口映射
逻辑层 协程 + Result 异常捕获、安全返回
扩展层 Token、多 BaseUrl 统一管理

🔧 这是一套可以直接落地于实际项目的通用网络架构,既简洁又灵活,适用于 MVVM、MVI 等主流架构。

相关推荐
安卓理事人6 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学7 小时前
Android M3U8视频播放器
android·音视频
q***57747 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober8 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
王六岁8 小时前
UIAutomatorViewer 安装指南 (macOS m3pro 芯片)
android studio
城东米粉儿9 小时前
关于ObjectAnimator
android
zhangphil9 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我10 小时前
从头写一个自己的app
android·前端·flutter
饕餮争锋11 小时前
Kotlin: [Internal Error] java.lang.NoSuchFieldError: FILE_HASHING_STRATEGY
java·kotlin
lichong95111 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端