1.基础的数据类

data class BaseResponse<T>(
    val code: Int,             // 状态码
    val message: String,       // 提示信息
    val data: T?               // 具体数据(泛型参数,类型灵活变化)
)
data class WeatherData(val main: Main,          // 温度等核心信息
                       val weather: List<Weather>, // 天气描述
                       val name: String   )
// 温度等信息
data class Main(
    val temp: Double,        // 温度
    val humidity: Int        // 湿度
)

// 天气描述
data class Weather(
    val description: String  // 描述(如"晴朗")
)

2.请求类

RequestApiClient
object RequestApiClient {
    // 基础 URL(根据实际接口修改)
    private const val BASE_URL = "https://api.openweatherma.org/data/2.5/"

    // OkHttpClient 实例(全局单例)
    private val okHttpClient: OkHttpClient by lazy {
        // 日志拦截器(开发环境启用)
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }

        OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(15, TimeUnit.SECONDS)
            .addInterceptor(loggingInterceptor) // 添加拦截器
            .build()
    }

    // Retrofit 实例(全局单例)
    val retrofit: Retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient) // 关联 OkHttpClient
            .addConverterFactory(GsonConverterFactory.create()) // Gson 解析
            .build()
    }
}

3.ApiService

interface ApiService {
    @GET("weather")
    suspend fun getWeather(
        @Query("q") city: String,
        @Query("appid") apiKey: String
    ): BaseResponse<WeatherData>

}

4.请求结果密封类

sealed class Result<out T> {

    // 加载中状态(适用于需要展示加载动画的场景)
    object Loading : Result<Nothing>()

    // 成功状态(携带数据)
    data class Success<out T>(val data: T) : Result<T>()

    // 失败状态(细分异常类型)
    sealed class Failure : Result<Nothing>() {
        // 网络异常
        data class NetworkError(val exception: Exception) : Failure()

        // 解析异常(如JSON格式错误)
        data class ParseError(val exception: Exception) : Failure()

        // 业务错误(如服务器返回的错误码:401未授权、404不存在)
        data class BusinessError(val code: Int, val message: String) : Failure()

        // 其他未知错误
        data class UnknownError(val exception: Exception) : Failure()
        data class NoDataError(val noData: String) : Failure()
    }

    /**
     * 判断当前结果是否为成功状态
     */
    val isSuccess: Boolean
        get() = this is Success

    /**
     * 判断当前结果是否为失败状态
     */
    val isFailure: Boolean
        get() = this is Failure

    /**
     * 判断当前结果是否为加载中状态
     */
    val isLoading: Boolean
        get() = this is Loading
}

// 扩展函数:安全获取成功数据(失败时返回null)
fun <T> Result<T>.getDataOrNull(): T? {
    return (this as? Result.Success<T>)?.data
}

// 扩展函数:获取失败信息(非失败状态返回null)
fun Result<*>.getErrorMsgOrNull(): String? {
    return when (this) {
        is Result.Failure.NetworkError -> "网络异常:${exception.message}"
        is Result.Failure.ParseError -> "数据解析错误:${exception.message}"
        is Result.Failure.BusinessError -> "业务错误($code):$message"
        is Result.Failure.UnknownError -> "未知错误:${exception.message}"
        is Result.Failure.NoDataError -> "数据为空"
        else -> null
    }
}


/**
 * Result 扩展函数:处理失败状态
 */
fun Result<*>.handleFailure(context: Context) {
    if (this is Result.Failure) {
        val errorMsg = when (this) {
            is Result.Failure.NetworkError -> "网络异常"+exception.message
            is Result.Failure.ParseError -> "数据解析错误,请稍后重试"
            is Result.Failure.BusinessError -> "操作失败:${message}(错误码:${code})"
            is Result.Failure.UnknownError -> "未知错误:${exception.message ?: "请联系客服"}"
            is Result.Failure.NoDataError -> "数据为空"
        }
        Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()

    }
}

5.请求仓库Repository

class WeatherRepository(private val weatherApi: ApiService) {
    // 获取天气数据(suspend 函数,在协程中调用)
    suspend fun fetchWeather(city: String, apiKey: String): Result<WeatherData> {
        return try {
            // 调用接口(通过单例的 weatherApi)
            val response = weatherApi.getWeather(city, apiKey)

            // 根据接口返回的 code 处理结果
            if (response.code == 0) {
                response.data?.let {
                    Result.Success(it) // 成功:返回具体数据
                } ?: Result.Failure.NoDataError("数据为空")
            } else {
                // 业务错误(如 code 非 200)
                Result.Failure.BusinessError(code = response.code, message = response.message)
            }
        } catch (e: Exception) {
            // 捕获异常(网络错误、解析错误等)
            when(e){
                is JsonParseException -> {
                    Result.Failure.ParseError(e)
                }
                is IOException -> {
                    Result.Failure.NetworkError(e)
                }
                else -> {
                    Result.Failure.UnknownError(e)
                }
            }
        }
    }
}

6.ViewModel

class MainActivityViewModel: ViewModel() {
    // 创建接口实例
    val apiService: ApiService = RequestApiClient.retrofit.create(ApiService::class.java)
    val repository = WeatherRepository(apiService)
    private val _weatherState = MutableStateFlow<Result<WeatherData>>(Result.Loading)
    val weatherState: StateFlow<Result<WeatherData>> = _weatherState

    fun loadWeather(city: String, apiKey: String) {
        viewModelScope.launch {
            _weatherState.value = Result.Loading
            val result = repository.fetchWeather(city, apiKey)
            _weatherState.value = result
        }
    }
}

7.Activity

class MainActivity : AppCompatActivity() {
    private val viewModel: MainActivityViewModel by viewModels()
    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
        }
        observeResult()
        viewModel.loadWeather("1","2")
    }
    fun observeResult(){
        lifecycleScope.launch {
            viewModel.weatherState.collect { result ->
                when (result) {
                    is Result.Loading -> {
                        // 显示加载动画
                        //progressBar.visibility = View.VISIBLE
                    }
                    is Result.Success<WeatherData> -> {
                        // 隐藏加载,展示数据
                        //progressBar.visibility = View.GONE
                        val weatherData = result.data
                        //updateUi(weatherData)
                    }
                    //统一处理错误
                    is Result.Failure -> {
                        //Toast.makeText(this@MainActivity,result.getErrorMsgOrNull(), Toast.LENGTH_SHORT).show()

                        result.handleFailure(this@MainActivity);
                    }
                }
            }
        }
    }
}

完整下载链接:  RetrofitDemo: Retrofit使用案例

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐