基于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




