#android #firebase #kotlin #firebase-realtime-database
#Android #firebase #kotlin #firebase-база данных в реальном времени
Вопрос:
Я создаю приложение для чата с использованием Kotlin и firebase и запускаю приведенные ниже функции для создания списка чатов во внешнем интерфейсе. Он отлично работает при загрузке. Но в ту секунду, когда пользователь отправляет сообщение, он повторяет вывод столько же раз, сколько элементов, уже находящихся в массиве mUsers? Есть мысли о том, что я здесь делаю не так?
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_messages, container, false)
recycler_view_msgList = view.findViewById(R.id.recycler_view_msglist)
recycler_view_msgList.setHasFixedSize(true)
recycler_view_msgList.layoutManager = LinearLayoutManager(context)
firebaseUser = FirebaseAuth.getInstance().currentUser
usersMsgList = ArrayList()
val ref = FirebaseDatabase.getInstance().reference.child("ChatList").child(firebaseUser!!.uid).orderByChild(
"chattimeStmp"
)
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
(usersMsgList as ArrayList).clear()
for (dataSnapshot in p0.children) {
val messageList = dataSnapshot.getValue(MessageList::class.java)
(usersMsgList as ArrayList).add(messageList!!)
}
(usersMsgList as ArrayList<MessageList>).reverse()
retrieveMessageList()
}
override fun onCancelled(p0: DatabaseError) {
}
})
updateToken(FirebaseInstanceId.getInstance().token)
return view
}
private fun retrieveMessageList() {
mUsers = ArrayList()
(mUsers as ArrayList).clear()
val arraysize = usersMsgList!!.size
val i = 0
for (eachMessageList in usersMsgList!!) {
if (eachMessageList.getId() != null) {
val receiverId = eachMessageList.getId()
if (receiverId != null) {
val ref =
FirebaseDatabase.getInstance().reference.child("Users").orderByChild("uid").equalTo(receiverId)
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
for (dataSnapshot in p0.children) {
val user = dataSnapshot.getValue(Users::class.java)
if (user != null) {
(mUsers as ArrayList).add(user)
}
}
userMsgAdapter = UserMsgAdapter(context!!, mUsers as ArrayList<Users>,true)
userMsgAdapter!!.notifyDataSetChanged()
recycler_view_msgList.adapter = userMsgAdapter
}
override fun onCancelled(p0: DatabaseError) {
}
}
)
}
}
}
}
Ответ №1:
Насколько я могу судить, вы получаете сообщения несколько раз. На самом деле, ваш код может быть намного проще:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_messages, container, false)
recycler_view_msgList = view.findViewById(R.id.recycler_view_msglist)
recycler_view_msgList.setHasFixedSize(true)
recycler_view_msgList.layoutManager = LinearLayoutManager(context)
firebaseUser = FirebaseAuth.getInstance().currentUser
usersMsgList = ArrayList()
userMsgAdapter = UserMsgAdapter(context!!, mUsers as ArrayList<Users>,true)
recycler_view_msgList.adapter = userMsgAdapter
val ref = FirebaseDatabase.getInstance().reference.child("ChatList").child(firebaseUser!!.uid).orderByChild(
"chattimeStmp"
)
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
(usersMsgList as ArrayList).clear()
for (dataSnapshot in p0.children) {
val messageList = dataSnapshot.getValue(MessageList::class.java)
(usersMsgList as ArrayList).add(messageList!!)
}
(usersMsgList as ArrayList<MessageList>).reverse()
userMsgAdapter!!.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
throw error.toException() // never ignore errors
}
})
updateToken(FirebaseInstanceId.getInstance().token)
return view
}
retrieveMessageList
Из того, что я вижу, с помощью приведенного выше кода вы можете полностью удалить. Вы просто создаете адаптер и регистрируете прослушиватель для сообщений один раз, onCreateView
а затем сообщаете адаптеру, когда данные были изменены.
Комментарии:
1. Привет, Фрэнк, спасибо, что посмотрели на это, я использую retrieveMessageList, чтобы получить подробную информацию о каждом пользователе, присутствующем в userMsgList. Если я не запускаю retrieveMessageList, mUsers пуст, а ChatList будет пустым…
2. Ах, извините, я это пропустил. Но в представлении отображается список сообщений, верно? В любом случае: вы должны подключить адаптер,
onCreateView
как я сделал здесь, и далееonDataChange
, поскольку он вызывается повторно. Обычно вам требуется только один экземпляр адаптера на весь срок службы представления,.3. ах, да, представление сбивает с толку, пришлось замаскировать, извините, это список чатов, у меня запущено два адаптера — один для получения списка чатов, которые есть у пользователя, и два, чтобы заставить пользовательские объекты заполнять представление списка чатов на интерфейсе. Проблема, на мой взгляд, заключается в том, что я пытаюсь выполнить цикл запроса, запрашивая конкретного пользователя, а не загружая весь узел Users, а затем заполняя mUsers, является ли мой подход неправильным? — внешний цикл находится в массиве объектов userMsgList…
4. В общем, это антипаттерн:
FirebaseDatabase.getInstance().reference.child("Users").orderByChild("uid").equalTo(receiverId)
. При хранении профилей пользователей рекомендуется использовать их UID в качестве ключа. При этом приведенный выше код становится:FirebaseDatabase.getInstance().reference.child("Users").child(receiverId)
и вам больше не нужен циклonDataChange
(поскольку вы уже читаете один конкретный узел).5. антипаттерн — о, хорошо, тогда я отойду от этого подхода, буду делать это шаг за шагом, спасибо, Фрэнк, ура