Come fare LiveData<MutableList<T>> aggiornamento quando si modifica una proprietà di T?

0

Domanda

Sto facendo un'app che ottiene la (pseudo) latenza valori mediante richiesta di alcuni url e la registrazione del tempo che vorrà.

Primo, io uso retrofit per ottenere una risposta JSON da un server web. Questa risposta contiene: il nome dell'host (ad esempio Ebay UK), l'url dell'host (ad es. www.ebay.co.uk), e l'url di un'immagine. Ho una mappa di questa risposta sulla mia classe di dati che è simile alla seguente:

data class(
    val name: String,
    var url: String,
    val icon: String,
    var averagePing: Long = -1
)

url è una var di proprietà come prima di effettuare le chiamate per ottenere i valori di latenza, ho bisogno di aggiungere https:// per fare la richiesta.

Sto facendo tutto questo, in questo modo:

fun getHostsLiveData() {
    viewModelScope.launch(Dispatchers.IO) {
        val hostList = repo.getHosts()
        for (host in hostList) {
            host.url = "https://" + host.url
            host.averagePing = -1
        }
        hostListLiveData.postValue(hostList)//updated the recyclerview with initial values
        //with default (-1) value of averagePing

        for (host in hostList) {
            async { pingHostAndUpdate(host.url, hostList) }
        }
    }
}

Il primo ciclo for prepara i miei dati. La linea dopo il ciclo for, invia i dati al centro di riciclaggio adattatore, per mostrare il nome host e l'url e l'icona subito (tutto questo funziona, cioè io ho un lavoro di osservatore per il LiveData), mentre sono in attesa per la latenza valori.

Il secondo ciclo for chiama la funzione per calcolare i valori di latenza per ogni host e il updateHostList() la funzione aggiorna il LiveData.

Questo è come le funzioni look:

suspend fun pingHostAndUpdate(url: String, hostList: MutableList<Host>) {
    try {
        val before = Calendar.getInstance().timeInMillis
        val connection = URL(url).openConnection() as HttpURLConnection //Need error handling
        connection.connectTimeout = 5*1000
        connection.connect()
        val after = Calendar.getInstance().timeInMillis
        connection.disconnect()
        val diff = after - before
        updateHostList(url, diff, hostList)
    } catch (e: MalformedURLException) {
        Log.e("MalformedURLExceptionTAG", "MalformedURLException")
    } catch (e: IOException) {
        Log.e("IOExceptionTAG", "IOException")
    }
}

fun updateHostList(url: String, pingResult: Long, hostList: MutableList<Host>) {
    //All this on mainThread
    var foundHost: Host? = null
    var index = 0
    for (host in hostListLiveData.value!!) { 
        if (host.url == url) {
            foundHost = host
            break
        }
        index++
    } 
    if (foundHost != null) {
        viewModelScope.launch(Dispatchers.Main) {
            val host =  Host(foundHost.name, foundHost.url, foundHost.icon, pingResult)
            Log.d("TAAAG", "$host") 
            hostList[index] = host
            hostListLiveData.value = hostList
        }
    }
}

Tutto questo accade nel viewModel. Attualmente sto aggiornando la mia lista inviando l'intero elenco di nuovo quando si modifica una proprietà di un elemento della lista, che sembra orribile per me.

La mia domanda è: Come posso aggiornare solo la proprietà di host e farlo aggiornare l'interfaccia utente, automaticamente?

Grazie in anticipo

Edit: il Mio osservatore assomiglia a questo:

viewModel.hostListLiveData.observe(this, Observer { adapter.updateData(it) })

E updateData() assomiglia a questo:

fun updateData(freshHostList: List<Host>) {
    hostList.clear()
    hostList.addAll(freshHostList)
    notifyDataSetChanged()
}

@ArpitShukla, ti suggeriscono avrei 2 funzioni di aggiornamento? uno per mostrare la lista iniziale e un altro per aggiornare voce di elenco? O dovrei solo mettere entrambi notifyDataSetChanged() e notifyItemChanged() in updateData()?

Edit2: ha cambiato la mia chiamata di funzione per rendere asincrono.

android-livedata kotlin
2021-11-23 22:53:04
1

Migliore risposta

1

Si può considerare per aggiornare gli elementi osservati da hostListLiveData utilizzando notifyItemChanged(position) invece notifyDataSetChanged() nel adapter.

notifyItemChanged(position) è un elemento modificare evento, che si aggiornano solo il contenuto dell'elemento.

EDIT:
Stai usando notifyDataSetChanged() aggiornamento del contenuto dei dati che provocano a relayout e riassociare il RecyclerView che non te lo aspettavi. Pertanto, si dovrebbe aggiornare il contenuto dei vostri dati tramite notifyItemChanged(position).

Penso che si potrebbe creare una nuova funzione per l'aggiornamento RecyclerView nella scheda di ad es.

fun updateHostAndPing(updatedHost: Host, position: Int) {
    hostList[position].apply {
        url = updatedHost.url
        averagePing = updatedHost.averagePing
    }
    notifyItemChanged(position)
}

e nel tuo osservatore, potrebbe essere necessario verificare se si è freschi lista o un elenco aggiornato e

viewModel.hostListLiveData.observe(this, Observer { 
    if (adapter.itemCount == ZERO) {
        adapter.updateData(it) 
    } else {
        it.forEachIndexed { index, host ->
            adapter.updateHostAndPing(host, index) 
        }
    }
})
2021-11-24 23:12:08

Questo funziona, ma non credo che questo è legato alla LiveData. Ho fatto una semplice recycler, che visualizza un elenco di eseguire questo test, e mi ha appena chiamato adattatore.notifyItemChanged(posizione). Questo ha fatto il lavoro ma non vedo come sia collegato a LiveData. Potresti chiarire per favore? P. S.: io aggiornare la questione mostrando come il mio osservatore funziona, penso che dare un qualche contesto
SpawnTheTronix

Sì, non si tratta di LiveData, è a causa del modo di aggiornare il RecyclerView. Si utilizza notifyDataSetChanged() aggiornamento del contenuto dei dati (ad esempio l'aggiornamento host e ping). Il notifyDataSetChanged() sarà completamente rifare la connessione e relayout tutti i dati visibili.
Putra Nugraha

Ho anche provato ad utilizzare ListAdapter invece di RecyclerView.Adapterha raggiunto il mio desiderato la funzionalità. Sapete cosa c'è di meglio, utilizzando notifyDataSetChanged() o un ListAdapter? Da quanto ho capito notifyDataSetChanged()si aggiorna la vista (riga RecyclerView) che dite di aggiornamento.ListAdapter controlla le differenze nella lista nuova e la vecchia lista, e quindi aggiorna il campo modificato (cioè un TextViewil nuovo valore (anche se non so se si aggiorna il solo TextView o l'intera riga, nel qual caso non ci sarebbe nessuna differenza?).
SpawnTheTronix

ListAdapter sotto il cofano è utilizzando AsyncListDiffer per aiutare a calcolare le differenze tra i dati memorizzati e fornito i dati, e come confrontare i dati si basano sulla condizione definita nel DiffUtil.ItemCallback. Per quanto ne so, ListAdapter non relayout tuo RecyclerViewma solo aggiornare i dati modificati. Bene, ListAdapter è anche una valida soluzione per il tuo caso comunque
Putra Nugraha

In altre lingue

Questa pagina è in altre lingue

Русский
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................