You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于OkHttp的Android REST API客户端重构需求咨询

重构OkHttp REST API客户端:支持JSON请求 + 轻松在UI层获取响应

刚好我之前在项目里做过类似的重构,分享一个实用的方案给你——既保留OkHttp的核心能力,又封装成支持JSON请求的通用客户端,还能让Activity/Fragment方便地拿到响应结果。

核心设计思路

  • 封装单例的ApiClient,统一管理OkHttp配置(超时、拦截器等)
  • 集成JSON序列化/反序列化工具(比如Gson或Moshi),自动处理请求体和响应体的JSON转换
  • 用协程(Coroutines)替代传统回调,让UI层代码更简洁易读
  • 结合ViewModel+LiveData,让请求逻辑与UI解耦,符合Android架构规范

1. 实现通用ApiClient

首先创建单例的ApiClient,封装OkHttp的请求逻辑,支持GET/POST的JSON请求:

import com.google.gson.Gson
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.logging.HttpLoggingInterceptor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit

class ApiClient private constructor() {
    // 配置OkHttpClient,可根据需求添加拦截器、超时等
    private val okHttpClient: OkHttpClient = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) // 调试用日志拦截器
        .build()

    private val gson = Gson() // 用于JSON转换,也可以替换成Moshi

    companion object {
        // 单例模式,懒加载初始化
        val instance: ApiClient by lazy { ApiClient() }
    }

    /**
     * 通用POST请求:发送JSON请求体,返回泛型响应
     * @param url API端点地址
     * @param requestBody 请求体数据类
     * @param responseType 响应体的Class类型
     */
    suspend fun <T> post(url: String, requestBody: Any, responseType: Class<T>): Result<T> {
        return withContext(Dispatchers.IO) {
            try {
                // 将请求体转换为JSON
                val jsonBody = RequestBody.create(
                    MediaType.parse("application/json; charset=utf-8"),
                    gson.toJson(requestBody)
                )

                val request = Request.Builder()
                    .url(url)
                    .post(jsonBody)
                    .addHeader("Content-Type", "application/json")
                    .build()

                val response = okHttpClient.newCall(request).execute()
                if (response.isSuccessful) {
                    response.body()?.string()?.let { json ->
                        val data = gson.fromJson(json, responseType)
                        Result.success(data)
                    } ?: Result.failure(NullPointerException("Response body is null"))
                } else {
                    Result.failure(ApiException(response.code(), response.message()))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }
    }

    /**
     * 通用GET请求:返回泛型响应
     * @param url API端点地址
     * @param responseType 响应体的Class类型
     */
    suspend fun <T> get(url: String, responseType: Class<T>): Result<T> {
        return withContext(Dispatchers.IO) {
            try {
                val request = Request.Builder()
                    .url(url)
                    .get()
                    .build()

                val response = okHttpClient.newCall(request).execute()
                if (response.isSuccessful) {
                    response.body()?.string()?.let { json ->
                        val data = gson.fromJson(json, responseType)
                        Result.success(data)
                    } ?: Result.failure(NullPointerException("Response body is null"))
                } else {
                    Result.failure(ApiException(response.code(), response.message()))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }
    }

    // 自定义API异常,方便UI层处理错误
    class ApiException(val code: Int, message: String?) : Exception(message)
}

2. 在UI层(Activity/Fragment)使用

推荐结合ViewModel来处理请求逻辑,让UI层只负责观察结果,避免内存泄漏:

第一步:创建ViewModel处理请求

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class UserViewModel : ViewModel() {
    // 用LiveData通知UI层请求结果
    private val _userLiveData = MutableLiveData<UserResponse?>()
    val userLiveData = _userLiveData

    private val _errorLiveData = MutableLiveData<String?>()
    val errorLiveData = _errorLiveData

    fun createUser(request: UserRequest) {
        viewModelScope.launch {
            val result = ApiClient.instance.post(
                "https://your-api-domain.com/api/users",
                request,
                UserResponse::class.java
            )

            result.onSuccess { response ->
                _userLiveData.postValue(response)
            }.onFailure { e ->
                val errorMsg = if (e is ApiClient.ApiException) {
                    "API Error ${e.code}: ${e.message}"
                } else {
                    "Request failed: ${e.message}"
                }
                _errorLiveData.postValue(errorMsg)
            }
        }
    }

    // 示例请求数据类
    data class UserRequest(val name: String, val email: String)
    // 示例响应数据类
    data class UserResponse(val id: Int, val name: String, val email: String)
}

第二步:在Activity中观察结果

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: UserViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProvider(this)[UserViewModel::class.java]

        // 观察成功响应
        viewModel.userLiveData.observe(this) { response ->
            response?.let {
                Toast.makeText(this, "User created: ${it.name}", Toast.LENGTH_SHORT).show()
                // 更新UI,比如显示用户信息
            }
        }

        // 观察错误信息
        viewModel.errorLiveData.observe(this) { errorMsg ->
            errorMsg?.let {
                Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
            }
        }

        // 触发请求
        val request = UserViewModel.UserRequest("Alice", "alice@example.com")
        viewModel.createUser(request)
    }
}

3. 额外优化建议

  • 替换JSON工具:如果不想用Gson,可以换成Moshi,只需要修改ApiClient里的序列化逻辑即可
  • 全局拦截器:可以添加Authorization头拦截器,统一处理token验证
  • 请求取消:在ViewModel的onCleared()方法中取消未完成的请求,避免内存泄漏
  • 统一错误处理:可以封装一个BaseViewModel,统一处理API错误和网络异常,减少重复代码

内容的提问来源于stack exchange,提问作者localhost

火山引擎 最新活动