搭建网络请求框架(Flow+Kotlin+Retrofit+GSON)

1,684 阅读5分钟

1、前言

最近在开发中,我的同事要求我讲解Retrofit的使用方法。考虑到只讲Retrofit可能会显得单调乏味,因此我决定封装一个网络请求框架,使用Kotlin、Retrofit、GSON和Flow技术实现。毕竟,作为程序员,我们常常要在封装和骂封装之间寻找平衡。不过需要注意的是,阅读本系列文章需要读者对Kotlin、Retrofit、GSON、Flow等技术有一定的了解和基本使用能力。我将从一个非常简单的例子和需求开始,逐步提出更复杂的需求,并一步步改进代码和设计。希望这个系列能为大家带来帮助。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

从0.1开发搭建网络请求框架(Flow+Kotlin+Retrofit+GSON)

从0.1开发搭建网络请求框架 2

2、介绍

先简单介绍一下这些工具的作用:

  • Kotlin:懂的都懂

  • Retrofit:懂的都懂

  • GSON:懂的都懂

  • Flow:懂的都懂

    相信你已经知道这是些什么东西,以及知道怎么用了

需求:刚开始我们几乎没有任何设计、优化,只为了请求一个GET接口并把数据显示到页面

3、添加依赖

因为我本身Demo使用的是Kotlin的DSL,写法可能和build.gradle有一些出入

dependencies {
    // Kotlin
    implementation("androidx.core:core-ktx:1.9.0")
    implementation("androidx.activity:activity-ktx:1.6.1")
    implementation("androidx.fragment:fragment-ktx:1.5.5")
    // Retrofit 库
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
    // Kotlin 协程库
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1")
}
---------------------or---------------------
dependencies {
    // Kotlin
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
    // Retrofit 库
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.9.2'
    // Kotlin 协程库
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}

4、定义数据模型类

根据接口返回的数据格式,定义一个数据模型类,用于解析接口返回的数据

data class BannerResponse(
    val data: List<Banner>?,
    val errorCode: Int,
    val errorMsg: String
)
data class Banner(...)

一般这个阶段需要跟服务器沟通,这里我们直接使用玩安卓提供的API,所以直接自己看就行了

5、定义 Retrofit 接口

使用 Retrofit,需要定义一个接口,用于描述 API 的调用方式,以及每个接口支持的请求方式、请求参数和响应结果。这里我们根据玩安卓提供的接口,定义一个 ApiService 接口,用于获取banner数据

interface ApiService {
    /**
     * https://www.wanandroid.com/banner/json
     */
    @GET("banner/json")
    suspend fun getBanners(): BannerResponse
}

6、创建 Retrofit 实例

创建了一个 Retrofit 实例,并指定了 API 的基础 URL 和数据解析方式。然后,我们我通过Kotlin的by语法糖创建 ApiService 的实例

private val retrofit: Retrofit by lazy {
       val gson = GsonBuilder().create()
       Retrofit.Builder()
           .baseUrl("https://www.wanandroid.com/")
           .addConverterFactory(GsonConverterFactory.create(gson))
           .build()
   }
​
private val apiService: ApiService by lazy { retrofit.create(ApiService::class.java) }

7、第一次调试

我们使用协程异步地执行网络请求,并在回调方法中更新界面。如果data不为空,我们从响应体中获取用户数据,并将数据显示在 TextView 上

private val retrofit: Retrofit by lazy {
       Retrofit.Builder()
           .baseUrl("https://www.wanandroid.com/")
           .addConverterFactory(GsonConverterFactory.create(gson))
           .build()
   }
private val gson = GsonBuilder().setPrettyPrinting().create()
private val apiService: ApiService by lazy { retrofit.create(ApiService::class.java) }
​
class MainArtActivity : AppCompatActivity() {
    private lateinit var btnBanner: Button
    //显示结果
    private lateinit var tvResult: TextView
    //格式化JSON
    private val gson = GsonBuilder().setPrettyPrinting().create()
    //请求API实例
    private val apiService: ApiService by lazy { RetrofitClient.create() }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_art)
        btnBanner = findViewById(R.id.btn_start)
        tvResult = findViewById(R.id.tv_result)
        btnBanner.setOnClickListener {
            //启动一个协程
            lifecycleScope.launch {
                //调用接口
                val response = apiService.getBanners()
                // 对获取的数据进行处理
                response.data?.let {
                    tvResult.text = gson.toJson(it)
                }
            }
        }
    }
}

显示如下 image-20230220181549195

就这样,进行网络请求并将数据显示在 Android 页面上的简单例子就好了

8、思考问题(重点)

  • 可以考虑将BannerResponse和其它类似的XXXResponse合并成一个通用的基类,例如BaseResponse,这样可以避免代码中出现过多的重复定义。
  • 如果一个结构需要多个数据类,可以考虑将其定义为一个复合数据类,以避免过多的嵌套结构,提高代码的可读性和可维护性。例如,可以定义一个Banner类,其中包含一个BannerResponse类和一个BannerData类,以分别表示响应和实际数据。
  • 为了避免代码中出现过多的重复代码和创建Retrofit实例的混乱情况,可以考虑将Retrofit的创建过程封装成一个单例或静态工厂类,以实现全局统一的Retrofit实例管理。
  • 为了避免代码中出现错误和异常导致应用程序崩溃或数据不一致的情况,可以加入错误处理和错误响应的统一处理机制,以提高代码的健壮性和稳定性。
  • 为了提高应用程序的性能和稳定性,可以在网络请求框架中加入缓存机制和重试机制(当然还可能有预取机制、图片压缩等优化技术),以优化网络请求的处理过程。
  • 也不存在请求拦截和返回拦截、OAuth认证、Cookie管理

9、下个篇章

因为篇幅原因,我们先到这,在第八点中,我们埋下了许许多多的坑,我们也将在这个系列,一一埋入。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

10、感谢

  1. 校稿:ChatGpt/Bing
  2. 文笔优化:ChatGpt/Bing
  3. 玩安卓API

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情