You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

真机从导航键恢复应用时onResume卡顿及RecyclerView重复加载求助

问题描述

真机测试应用时,onStart阶段运行正常,但将应用置于onPause状态打开其他应用后,点击导航键恢复应用时响应迟缓,存在约1秒延迟,且每次恢复应用时,Fragment中的RecyclerView都会重新加载。

相关代码(ChatListFragment.kt)
class ChatListFragment : Fragment() {
    private lateinit var binding: FragmentChatListBinding
    private var chatList = ArrayList<Chats>()
    private var user: Chats?= null
    private var chatListAdapter = ChatListAdapter(chatList)
    private lateinit var auth: FirebaseAuth
    private lateinit var storage: FirebaseStorage
    private lateinit var firestore: FirebaseFirestore

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view =  inflater.inflate(R.layout.fragment_chat_list, container, false)
        binding = FragmentChatListBinding.inflate(layoutInflater)
        init()
        addContacts()
        changeListener()
        // Calling the override functions from
        // the Linear Layout Manager inner class
        val myLinearLayoutManager = object : LinearLayoutManager(context) {
            override fun canScrollVertically(): Boolean {
                return false
            }
        }
        view.popChatsRec.layoutManager = myLinearLayoutManager
        view.popChatsRec.adapter = chatListAdapter
        chatList.clear()
        return view
    }
    private fun addContacts(){
        val firebaseUser = auth.currentUser
        if (firebaseUser != null){
            firestore.collection("Contacts").document(auth.currentUser?.uid!!).collection("rooms").get()
                .addOnSuccessListener {
                    if (!it.isEmpty){
                        for (documents in it.documents){
                            val uId = documents.data?.get("uId").toString()
                            val uName = documents.data?.get("fullName").toString()
                            val uNum = documents.data?.get("fullNum").toString()
                            val uStatus = documents.data?.get("status").toString()
                            val uPresence = documents.data?.get("presence").toString()
                            val uImg = documents.data?.get("image").toString()
                            chatList.add(Chats(uId, uImg, uName, "", uNum, uStatus, uPresence))
                        }
                    } else {
                        Toast.makeText(context, "FailedHere", Toast.LENGTH_SHORT).show()
                    }
                    chatListAdapter.notifyDataSetChanged()
                }.addOnFailureListener { Toast.makeText(context, "FailureOverall", Toast.LENGTH_SHORT).show() }
        } else{
            Toast.makeText(context, "Null", Toast.LENGTH_SHORT).show()
        }
    }
    private fun changeListener(){
        firestore.collection("Contacts").document(auth.currentUser?.uid!!).collection("rooms")
            .addSnapshotListener { snapshots, e->
            if (e != null){
                Log.w(ContentValues.TAG, "listen:error", e)
                return@addSnapshotListener
            }
            for (dc in snapshots!!.documentChanges) {
                if (dc.type == DocumentChange.Type.MODIFIED) {
                    chatList.clear()
                    addContacts()
                    Log.d(ContentValues.TAG, "New city: ${dc.document.data}")
                }
            }
        }
    }
    private fun init(){
        auth = FirebaseAuth.getInstance()
        storage = FirebaseStorage.getInstance()
        firestore = Firebase.firestore
    }
}
问题原因与修复方案

核心问题点

  1. RecyclerView重复加载:onCreateView在Fragment恢复时会重新执行,你在这里直接调用addContacts()触发全量数据请求,同时末尾的chatList.clear()会清空刚加载的数据,导致重复加载+界面空白。
  2. 恢复响应迟缓:每次恢复都重新发起Firestore网络请求,加上全量刷新UI,双重耗时导致卡顿;另外SnapshotListener未绑定生命周期,可能造成内存泄漏加重性能负担。

具体修复步骤

1. 调整数据加载时机,避免重复请求

把数据初始化逻辑移到onViewCreated,并判断列表是否为空再加载,同时移除onCreateView里的chatList.clear():

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    binding = FragmentChatListBinding.inflate(inflater, container, false)
    // 提前设置LayoutManager和Adapter
    val myLinearLayoutManager = object : LinearLayoutManager(context) {
        override fun canScrollVertically(): Boolean {
            return false
        }
    }
    binding.popChatsRec.layoutManager = myLinearLayoutManager
    binding.popChatsRec.adapter = chatListAdapter
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    init()
    // 仅列表为空时才加载数据,避免重复请求
    if (chatList.isEmpty()) {
        addContacts()
    }
    changeListener()
}

2. 优化SnapshotListener的生命周期与更新逻辑

绑定Fragment生命周期自动管理监听,且只更新修改的条目,而非全量刷新:

private fun changeListener(){
    firestore.collection("Contacts").document(auth.currentUser?.uid!!).collection("rooms")
        // 绑定View生命周期,Fragment销毁时自动取消监听
        .addSnapshotListener(viewLifecycleOwner) { snapshots, e->
        if (e != null){
            Log.w(ContentValues.TAG, "listen:error", e)
            return@addSnapshotListener
        }
        for (dc in snapshots!!.documentChanges) {
            if (dc.type == DocumentChange.Type.MODIFIED) {
                // 找到修改的条目并局部更新
                val targetId = dc.document.getString("uId")
                val index = chatList.indexOfFirst { it.uId == targetId }
                if (index != -1) {
                    val updatedChat = dc.document.toObject(Chats::class.java)
                    updatedChat?.let {
                        chatList[index] = it
                        chatListAdapter.notifyItemChanged(index)
                    }
                }
            }
        }
    }
}

3. 用DiffUtil优化Adapter刷新性能

替换notifyDataSetChanged为DiffUtil,减少不必要的UI重绘:

// 修改ChatListAdapter
class ChatListAdapter(private val chatList: MutableList<Chats>) : RecyclerView.Adapter<ChatListAdapter.ViewHolder>() {

    // ... 原有ViewHolder等代码

    fun updateData(newList: List<Chats>) {
        val diffCallback = ChatDiffCallback(chatList, newList)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        chatList.clear()
        chatList.addAll(newList)
        diffResult.dispatchUpdatesTo(this)
    }

    class ChatDiffCallback(private val oldList: List<Chats>, private val newList: List<Chats>) : DiffUtil.Callback() {
        override fun getOldListSize() = oldList.size
        override fun getNewListSize() = newList.size
        override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
            return oldList[oldPos].uId == newList[newPos].uId
        }
        override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
            return oldList[oldPos] == newList[newPos]
        }
    }
}

然后修改addContacts里的刷新逻辑:

private fun addContacts(){
    val firebaseUser = auth.currentUser
    if (firebaseUser != null){
        firestore.collection("Contacts").document(auth.currentUser?.uid!!).collection("rooms").get()
            .addOnSuccessListener { result ->
                val newChats = mutableListOf<Chats>()
                if (!result.isEmpty){
                    result.documents.forEach { doc ->
                        doc.toObject(Chats::class.java)?.let { newChats.add(it) }
                    }
                } else {
                    Toast.makeText(context, "暂无联系人", Toast.LENGTH_SHORT).show()
                }
                chatListAdapter.updateData(newChats)
            }.addOnFailureListener { 
                Toast.makeText(context, "加载联系人失败", Toast.LENGTH_SHORT).show() 
            }
    } else{
        Toast.makeText(context, "用户未登录", Toast.LENGTH_SHORT).show()
    }
}

4. 修复重复View绑定问题

原代码同时使用inflater.inflate和FragmentChatListBinding.inflate,属于重复操作,统一用ViewBinding即可,减少资源浪费。

内容的提问来源于stack exchange,提问作者Mahmoud Nabil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 02:18:02