Android用Retrofit+View Binding获取嵌套JSON数组适配RecyclerView
Android端Retrofit拉取嵌套JSON+ViewBinding+RecyclerView完整实现
以下实现基于Kotlin + 协程编写,Java开发逻辑完全对齐,仅存在语法差异。我们先约定待解析的嵌套JSON结构示例(实际开发替换为你的接口返回结构即可):
{ "code": 0, "message": "请求成功", "data": [ { "article_id": 1001, "title": "Android 开发入门", "author": "张三", "tags": ["移动端", "Kotlin", "入门教程"] // 嵌套的JSON数组 }, { "article_id": 1002, "title": "Retrofit 源码解析", "author": "李四", "tags": ["开源库", "源码", "网络请求"] } ] }
1. 前置依赖与配置
在Module级别的build.gradle文件中添加依赖,同时开启ViewBinding:
android { // ... 其他已有配置 buildFeatures { viewBinding true // 开启ViewBinding } } dependencies { // RecyclerView控件 implementation "androidx.recyclerview:recyclerview:1.3.2" // Retrofit 核心库 implementation "com.squareup.retrofit2:retrofit:2.9.0" // Gson 转换器,自动解析JSON为实体类 implementation "com.squareup.retrofit2:converter-gson:2.9.0" // 协程依赖,用于异步发起网络请求 implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" }
在AndroidManifest.xml中添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />
2. 定义对应嵌套结构的实体类
根据JSON结构编写Kotlin数据类,Gson会自动递归解析嵌套的数组/对象,不需要手动处理嵌套层级:
// 列表项实体类,对应data数组里的每个元素 data class ArticleItem( @SerializedName("article_id") val id: Int, @SerializedName("title") val title: String, @SerializedName("author") val author: String, @SerializedName("tags") val tags: List<String> // 直接映射嵌套的JSON数组 ) // 接口最外层返回实体 data class ApiResponse( @SerializedName("code") val code: Int, @SerializedName("message") val msg: String, @SerializedName("data") val articleList: List<ArticleItem> // 外层嵌套的JSON数组 )
3. 配置Retrofit接口与实例
首先定义网络请求接口:
interface ArticleApiService { @GET("article/list") // 替换为你的接口实际路径 suspend fun getArticleList(): ApiResponse }
编写Retrofit单例实例,避免重复创建资源浪费:
object RetrofitClient { // 替换为你的接口实际baseUrl,注意必须以/结尾 private const val BASE_URL = "https://your-api-domain.com/" val apiService: ArticleApiService by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(ArticleApiService::class.java) } }
4. 编写RecyclerView列表适配器
适配器直接使用item布局生成的ViewBinding类,完全避免手写findViewById:
首先写列表item的布局item_article.xml,放置文本控件展示标题、作者、标签即可:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold"/> <TextView android:id="@+id/tv_author" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textSize="14sp"/> <TextView android:id="@+id/tv_tags" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textSize="12sp"/> </LinearLayout>
然后编写适配器代码:
class ArticleAdapter : RecyclerView.Adapter<ArticleAdapter.ArticleViewHolder>() { private var dataList: List<ArticleItem> = emptyList() // 刷新列表数据 fun submitList(list: List<ArticleItem>) { dataList = list notifyDataSetChanged() } inner class ArticleViewHolder(val binding: ItemArticleBinding) : RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder { val binding = ItemArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ArticleViewHolder(binding) } override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) { val item = dataList[position] with(holder.binding) { tvTitle.text = item.title tvAuthor.text = "作者:${item.author}" // 处理嵌套的标签数组,拼接成字符串展示 tvTags.text = "标签:${item.tags.joinToString("、")}" } } override fun getItemCount(): Int = dataList.size }
5. Activity中完成最终逻辑串联
主布局activity_main.xml中放置RecyclerView控件即可:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_article" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>
在MainActivity中完成初始化、请求、数据加载逻辑:
class MainActivity : AppCompatActivity() { // ViewBinding实例 private lateinit var binding: ActivityMainBinding private lateinit var articleAdapter: ArticleAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 初始化RecyclerView articleAdapter = ArticleAdapter() binding.rvArticle.apply { layoutManager = LinearLayoutManager(this@MainActivity) adapter = articleAdapter } // 发起网络请求拉取数据 lifecycleScope.launch { runCatching { RetrofitClient.apiService.getArticleList() }.onSuccess { response -> if (response.code == 0) { // 请求成功,把嵌套数组里的数据提交给适配器展示 articleAdapter.submitList(response.articleList) } }.onFailure { // 这里处理请求失败逻辑,比如弹Toast提示 it.printStackTrace() } } } }
注意事项
- Retrofit的baseUrl必须以/结尾,否则会抛出IllegalArgumentException
- 嵌套JSON解析失败时,先通过日志打印原始返回内容,核对字段名、字段类型是否和实体类匹配
- RecyclerView必须设置LayoutManager,否则列表不会渲染
- 不要在主线程发起网络请求,使用协程或Retrofit自带的enqueue异步方法即可避免ANR
- 如果targetSdkVersion >= 28,默认禁止明文HTTP请求,接口是HTTP的需要额外配置网络安全规则
内容的提问来源于stack exchange,提问作者Ravi_Makani
相关产品推荐
相关产品推荐

