使用Retrofit@GET请求数据时遇No adapter attached错误求助
问题:Retrofit获取数据时RecyclerView提示“No adapter attached; skipping layout”
已确认网络正常,RecyclerView的layout_height设为wrap_content,相关代码及布局如下,请求排查原因:
MainActivity代码
package com.example.restart import android.content.ContentValues.TAG import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.recyclerview.widget.GridLayoutManager import com.example.restart.api.ApiRequest import com.example.restart.api.CatalogAdapter import com.example.restart.api.CatalogModel import com.example.restart.databinding.ActivityMainBinding import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import retrofit2.Retrofit import retrofit2.awaitResponse import retrofit2.converter.gson.GsonConverterFactory class MainActivity : AppCompatActivity() { private var binding: ActivityMainBinding? = null private var adapterCatalog = CatalogAdapter() lateinit var catalogList: ArrayList<CatalogModel> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding!!.root) getData() } private lateinit var allblock: List<CatalogModel> private fun initCategory(category: List<CatalogModel>) { with(binding!!){ listCatalogInMain.layoutManager = GridLayoutManager(this@MainActivity, 1) listCatalogInMain.adapter = adapterCatalog var cataloglist: List<CatalogModel> = category if(cataloglist.isNotEmpty()){ for(element in cataloglist){ adapterCatalog.addCatalog(element) } } } } private fun getData(){ val api = Retrofit.Builder() .baseUrl("https://medic.madskill.ru") .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiRequest::class.java) CoroutineScope(Dispatchers.IO).launch { try { val response = api.getCatalog().awaitResponse() if (response.isSuccessful) { val data = response.body()!! catalogList = data as ArrayList<CatalogModel> runOnUiThread { initCategory(catalogList) } Log.d(TAG, data.toString()) } } catch (e: Exception) { withContext(Dispatchers.Main) { Log.d(TAG, e.toString()) } } } } }
ApiRequest代码
import retrofit2.Call import retrofit2.http.GET interface ApiRequest { @GET("/api/catalog") fun getCatalog(): Call<List<CatalogModel>> }
Adapter代码
package com.example.restart.api import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.example.restart.R import com.example.restart.databinding.ItemBlockBinding class CatalogAdapter: RecyclerView.Adapter<CatalogAdapter.CategoryHolder>() { private val listCatalog = ArrayList<CatalogModel>() class CategoryHolder(item: View): RecyclerView.ViewHolder(item){ private var bindingCategory = ItemBlockBinding.bind(item) fun bind(category: CatalogModel){ bindingCategory.name.text = category.name bindingCategory.category.text = category.category bindingCategory.bio.text = category.bio bindingCategory.price.text = category.price bindingCategory.timeResult.text = category.time_result bindingCategory.preparation.text = category.preparation bindingCategory.description.text = category.description } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_block, parent, false) return CategoryHolder(view) } override fun getItemCount(): Int { return listCatalog.size } override fun onBindViewHolder(holder: CategoryHolder, position: Int) { holder.bind(listCatalog[position]) } fun addCatalog(category: CatalogModel){ listCatalog.add(category) notifyDataSetChanged() } }
布局代码
主布局中RecyclerView部分
<androidx.recyclerview.widget.RecyclerView android:id="@+id/listCatalogInMain" android:layout_width="match_parent" android:layout_height="wrap_content" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" tools:listitem="@layout/item_block"> </androidx.recyclerview.widget.RecyclerView>
Item布局
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> ..... ..... </androidx.constraintlayout.widget.ConstraintLayout>
问题排查与修复方案
1. 警告核心原因:RecyclerView初始化时机滞后
当前代码里,initCategory是在网络请求成功后才给RecyclerView设置Adapter和LayoutManager,而RecyclerView在页面首次渲染时(onCreate阶段)还没有任何Adapter关联,系统就会抛出这个警告。
修复: 在onCreate中提前初始化RecyclerView,不要等网络请求完成:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding!!.root) // 提前初始化RecyclerView的布局管理器和适配器 binding?.listCatalogInMain?.apply { layoutManager = GridLayoutManager(this@MainActivity, 1) adapter = adapterCatalog } getData() }
然后修改initCategory,只负责更新数据即可:
private fun initCategory(category: List<CatalogModel>) { // 先清空旧数据(如果需要刷新而非追加) adapterCatalog.listCatalog.clear() category.forEach { adapterCatalog.addCatalog(it) } }
2. Retrofit协程调用优化
你当前用Call配合awaitResponse(),可以改用suspend函数更贴合协程用法,同时避免不安全的强制转换:
- 修改ApiRequest接口:
import retrofit2.Response import retrofit2.http.GET interface ApiRequest { @GET("/api/catalog") suspend fun getCatalog(): Response<List<CatalogModel>> }
- 修改
getData中的调用逻辑:
private fun getData(){ val api = Retrofit.Builder() .baseUrl("https://medic.madskill.ru") .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiRequest::class.java) CoroutineScope(Dispatchers.IO).launch { try { val response = api.getCatalog() // 直接调用suspend函数,无需awaitResponse if (response.isSuccessful) { val data = response.body() ?: return@launch catalogList = ArrayList(data) // 安全转换为ArrayList,避免强制转换崩溃 runOnUiThread { initCategory(catalogList) } Log.d(TAG, data.toString()) } } catch (e: Exception) { withContext(Dispatchers.Main) { Log.d(TAG, e.toString()) } } } }
3. Adapter性能优化
当前逐个添加数据并每次调用notifyDataSetChanged()效率较低,建议新增批量添加方法:
// 在CatalogAdapter中添加 fun addAllCatalog(categories: List<CatalogModel>){ listCatalog.addAll(categories) notifyDataSetChanged() }
然后在initCategory中直接调用:
private fun initCategory(category: List<CatalogModel>) { adapterCatalog.listCatalog.clear() adapterCatalog.addAllCatalog(category) }
4. 布局冗余清理
主布局中已经通过app:layoutManager设置了布局管理器,代码里又重复设置,建议统一一处设置即可,避免冗余。
内容的提问来源于stack exchange,提问作者klmnvan
相关产品推荐
相关产品推荐

