Kotlin删除Firebase文件后RecyclerView不刷新问题排查求助
问题描述
我是Kotlin新手,正在靠试错开发第一个App(之前功能都正常)。现在遇到的问题是:基于Firebase Storage文件实现的RecyclerView,每行的删除按钮能成功删除Firebase上的文件,但点击后列表不会刷新,已删除的文件还显示在列表里。之前用Room实现相同功能时列表能正常刷新,附上Fragment、ViewModel及Adapter代码,求帮忙找错。
Fragment代码
class SharingFragment : Fragment() { override fun onViewStateRestored(savedInstanceState: Bundle?) { super.onViewStateRestored(savedInstanceState) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { val binding: FragmentSharesBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_shares, container, false) val application = requireNotNull(this.activity).application val viewModelFactory = SharingViewModelFactory(application) val sharingViewModel = ViewModelProvider(this,viewModelFactory).get( SharingViewModel::class.java) sharingViewModel.getSharedWithMe().observe(this , Observer { list-> // set the layout manager and the adapter for the recycler view binding.listaRecyclerView.layoutManager = LinearLayoutManager(application) binding.listaRecyclerView.adapter = InsuranceAdapter(context!!,list) }) binding.sharingViewModel = sharingViewModel val root: View = binding.root return root } }
ViewModel(SharingViewModel)代码
class SharingViewModel( application: Application ) : AndroidViewModel(application) { var listReturn: MutableLiveData<List<String>> init { listReturn = MutableLiveData(listOf()) } fun getSharedWithMe() : MutableLiveData<List<String>> { var auth = Firebase.auth val storage = Firebase.storage var storageRef = storage.reference val databaseRef = storageRef.child("shared/${auth!!.currentUser!!.email}") var listFile = databaseRef.listAll() listFile.addOnSuccessListener { (items, prefixes) -> var list: MutableList<String> = mutableListOf() items.forEach { item -> list.add(item.name) } listReturn.value = listReturn.value?.plus(list) } .addOnFailureListener { // Uh-oh, an error occurred! } //Wait to receive the listall while(listFile.isComplete==false) {Thread.sleep(1000)} return listReturn } fun deleteSharedWithMe(file: String) { var auth = Firebase.auth val storage = Firebase.storage var storageRef = storage.reference val fileRef = storageRef.child("shared/${auth!!.currentUser!!.email}/${file}") fileRef.delete() listReturn.value = listReturn.value!!.toMutableList().apply { remove(file) }.toList() } }
Adapter代码
class InsuranceAdapter(val context : Context,val list : List<String>) : RecyclerView.Adapter<InsuranceAdapter.ViewHolder>() { // Inner ViewHolder class class ViewHolder(val binding : SharingListBinding) : RecyclerView.ViewHolder(binding.root){} // function to inflate the layout for each contact and create a new ViewHolder instance override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder( SharingListBinding.inflate(LayoutInflater.from(parent.context),parent,false) ) } // function to bind the data to the view elements of the ViewHolder @RequiresApi(Build.VERSION_CODES.O) override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.binding.fileTextview.text = list[position] // delete button onClickListener to delete the // file from firestore and notify the // adapter of the change holder.binding.deleteButton.setOnClickListener{ val application = requireNotNull(Application()) val sharingViewModel = SharingViewModel(Application()) //TODO borrar de Firebase sharingViewModel.deleteSharedWithMe(list[position]) // dao.delete(list[position]) //this was working when I was using Room and deleting from the DB was refreshing the list notifyItemChanged(position) notifyDataSetChanged() } } override fun getItemCount(): Int { return list.size } }
错误原因及修复方案
核心错误点
- Adapter创建独立ViewModel实例:点击删除按钮时,Adapter里新建的
SharingViewModel和Fragment绑定的ViewModel完全无关,修改的是新ViewModel的listReturn,Fragment观察的旧ViewModel LiveData根本没收到更新。 - Firebase删除与LiveData更新不同步:当前逻辑直接修改
listReturn,没有等待Firebase异步删除操作完成,且因为ViewModel实例不共享,Fragment的LiveData不会触发刷新。 - ViewModel线程阻塞问题:用
while循环+Thread.sleep等待Firebase异步操作,会阻塞UI线程导致ANR,且listReturn.value?.plus(list)会累加旧数据,多次调用会重复添加列表项。 - Fragment重复初始化Adapter:每次LiveData更新都重新创建Adapter,不仅浪费资源,Adapter持有的是旧列表快照,无法响应后续更新。
修复步骤
1. 让Adapter通过回调共享ViewModel
修改Adapter,添加删除回调接口,不再自行创建ViewModel:
class InsuranceAdapter( val context: Context, val list: List<String>, private val onDeleteClick: (String) -> Unit // 添加删除回调 ) : RecyclerView.Adapter<InsuranceAdapter.ViewHolder>() { class ViewHolder(val binding: SharingListBinding) : RecyclerView.ViewHolder(binding.root) {} override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder( SharingListBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) } @RequiresApi(Build.VERSION_CODES.O) override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.binding.fileTextview.text = list[position] holder.binding.deleteButton.setOnClickListener { onDeleteClick(list[position]) // 触发回调,由Fragment处理删除逻辑 } } override fun getItemCount(): Int { return list.size } }
2. 修正Fragment的Adapter初始化逻辑
Fragment中只设置一次LayoutManager,初始化Adapter时传入删除回调:
class SharingFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { val binding: FragmentSharesBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_shares, container, false) val application = requireNotNull(this.activity).application val viewModelFactory = SharingViewModelFactory(application) val sharingViewModel = ViewModelProvider(this, viewModelFactory).get( SharingViewModel::class.java) // 仅设置一次LayoutManager binding.listaRecyclerView.layoutManager = LinearLayoutManager(application) sharingViewModel.listReturn.observe(this, Observer { list -> // 传入删除回调,关联Fragment的ViewModel binding.listaRecyclerView.adapter = InsuranceAdapter(context!!, list) { fileName -> sharingViewModel.deleteSharedWithMe(fileName) } }) // 页面创建时主动获取一次列表 sharingViewModel.getSharedWithMe() binding.sharingViewModel = sharingViewModel return binding.root } }
3. 修复ViewModel逻辑问题
去掉线程阻塞,在Firebase删除成功后再更新LiveData,且每次获取列表时替换为新数据:
class SharingViewModel(application: Application) : AndroidViewModel(application) { private val _listReturn = MutableLiveData<List<String>>(emptyList()) val listReturn: LiveData<List<String>> = _listReturn // 对外暴露不可变LiveData fun getSharedWithMe() { val auth = Firebase.auth.currentUser ?: return val storageRef = Firebase.storage.reference.child("shared/${auth.email}") storageRef.listAll() .addOnSuccessListener { result -> val fileNames = result.items.map { it.name } _listReturn.value = fileNames // 直接替换为新列表,避免累加旧数据 } .addOnFailureListener { // 可添加错误提示,比如Toast.makeText(getApplication(), "获取列表失败", Toast.LENGTH_SHORT).show() } // 移除线程阻塞逻辑,异步操作由Firebase回调处理 } fun deleteSharedWithMe(file: String) { val auth = Firebase.auth.currentUser ?: return val fileRef = Firebase.storage.reference.child("shared/${auth.email}/$file") fileRef.delete() .addOnSuccessListener { // 删除成功后再更新LiveData _listReturn.value = _listReturn.value?.toMutableList()?.apply { remove(file) } ?: emptyList() } .addOnFailureListener { // 删除失败提示,比如Toast.makeText(getApplication(), "删除失败", Toast.LENGTH_SHORT).show() } } }
4. 确保ViewModelFactory正确传递参数
确认SharingViewModelFactory能正确传递Application:
class SharingViewModelFactory(private val application: Application) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(SharingViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return SharingViewModel(application) as T } throw IllegalArgumentException("未知的ViewModel类") } }
内容的提问来源于stack exchange,提问作者Miguel A Felipe Rodriguez
相关产品推荐
相关产品推荐

