如何从 LiveData 加载 LiveData?

How to load a LiveData from a LiveData?

在我的数据库中有两个列表:聊天列表和用户列表。聊天列表中的每个项目都包含其各自用户的 ID。我需要将每个聊天与用户数据一起加载,以将其显示为 recyclerview 中的实时数据。如何在不破坏 MVVM 模式的情况下做到这一点?

数据库:

{
  "chats" : {
      "abc" : {
        "userId" : "123",
        "chatId" : "abc",
        "lastMessage" : "message1"
      },
      "cde" : {
        "userId" : "456",
        "chatId" : "def",
        "lastMessage" : "message2"
      }
    },
  "users" : {
    "abc" : {
      "name" : "Name1",
      "photo" : "photo.jpg",
      "userId" : "abc",
      "email" : "name1@gmail.com"
    },
    "def" : {
      "name" : "Name2",
      "photo" : "photo.jpg",
      "userId" : "def",
      "email" : "name2@gmail.com"
    }
  }
}  

视图模型:

class ChatsViewModel:ViewModel() {

    private val chatsRepository = ChatsRepository()
    private var userRepository = UserRepository()
    fun fetchChatsData(): LiveData<MutableList<Chat>> {
        val mutableData = MutableLiveData<MutableList<Chat>>()
        chatsRepository.getChatsData().observeForever { chatsList ->
            mutableData.value = chatsList
        }
        return mutableData
    }
    fun fetchUserData(): LiveData<User> {
        val mutableData = MutableLiveData<User>()
        userRepository.getUserData("USER_ID_HERE").observeForever { user ->
            mutableData.value = user
        }
        return mutableData
    }

}

片段:

class ChatTabFragment2 : Fragment() {

    private lateinit var fragmentView: View
    private val viewModel by lazy { ViewModelProvider(this).get(ChatsViewModel::class.java) }
    private lateinit var adapter:ChatsAdapter


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        fragmentView = inflater.inflate(R.layout.fragment_chat_tab, container, false)

        adapter = ChatsAdapter{
            openChat(it.userId)
        }
        fragmentView.recycler_view.setHasFixedSize(true)
        fragmentView.recycler_view.adapter = adapter

        observeData()

        return fragmentView
    }

    private fun observeData() {
        viewModel.fetchChatsData().observe(viewLifecycleOwner, { chatsList ->
            chatsList.let {
             adapter.submitList(it)
            }
        })
    }

聊天存储库:

class ChatsRepository {

    private var chatsReference = FirebaseDatabase.getInstance().reference
            .child(UserFirebase.id)
    private lateinit var valueEventListener: ValueEventListener

    fun getChatsData(): LiveData<MutableList<Chat>> {
        val mutableData = MutableLiveData<MutableList<Chat>>()

        valueEventListener = object :ValueEventListener{
            override fun onDataChange(snapshot: DataSnapshot) {
                val chatsList = mutableListOf<Chat>()
                for (ds in snapshot.children) {
                    ds.getValue(Chat::class.java)?.let { chat ->
                        chatsList.add(chat)
                    }
                }
                mutableData.value = chatsList
            }

            override fun onCancelled(error: DatabaseError) {
                
            }

        }
        chatsReference.addValueEventListener(valueEventListener)
        return mutableData
    }
}

用户存储库

class UserRepository {
    fun getUserData(userId:String): LiveData<User> {
        val mutableData = MutableLiveData<User>()
        FirebaseDatabase.getInstance().reference.child("usersRef").child(userId)
                .addListenerForSingleValueEvent(object :ValueEventListener{
                    override fun onDataChange(snapshot: DataSnapshot) {
                        mutableData.value = snapshot.getValue(User::class.java)
                    }
                    override fun onCancelled(error: DatabaseError) {}
                })
        return mutableData
    }
}

我的建议是对数据层(firebase 数据库)和表示层(前端)使用不同的模型。例如:

让我们调用数据库中的模型 ChatRemoteModelUserRemoteModel

data class ChatRemoteModel() {
    val userId: String,
    val chatId: String,
    val lastMessage: String
}

data class UserRemoteModel() {
    val name: String,
    val photo: String,
    val userId: String,
    val email: String   
}

我们将演示文稿所需的模型称为 ChatUser

data class Chat() {
    val user: User,
    val chatId: String,
    val lastMessage: String
}

data class User() {
    val name: String,
    val photo: String,
    val userId: String,
    val email: String   
}

现在在您的存储库 classes 中,您从数据库中检索数据并将它们映射到表示模型。

class ChatsRepository {

    ...

    private val userRepository = UserRepository()

    fun getChatsData(): LiveData<MutableList<Chat>> {
        val mutableData = MutableLiveData<MutableList<Chat>>()

        valueEventListener = object :ValueEventListener{
            override fun onDataChange(snapshot: DataSnapshot) {
                val chatsList = mutableListOf<Chat>()
                for (ds in snapshot.children) {
                    ds.getValue(ChatRemoteModel::class.java)?.let { chat ->
                        userRepository.getUser(chat.userId).observeForever { user ->
                            chatsList.add(
                                Chat(user, chat.chatId, chat.lastMessage)
                            )    
                        }                            
                    }
                }
                mutableData.value = chatsList
            }

            override fun onCancelled(error: DatabaseError) {
                
            }

        }
        chatsReference.addValueEventListener(valueEventListener)
        return mutableData
    }
}

您也可以 return User 而不是 UserRepository class 中的 UserRemoteModel。这看起来有点矫枉过正,因为这两个模型实际上是相同的,但它使您可以根据 ViewModel 的需要灵活地设计表示模型。

所以我的意思是,如果你对不同的层有不同的模型,那么你可以为数据层设计模型以有效地存储数据和传输数据,并为表现层设计模型以有效地显示UI.你可能想改进代码,(命名,防止重复值,使用映射器, 使用 LiveData 转换等),我提供的是支持我的观点的最小示例。