Tworzę aplikację, która pobiera wartości (pseudo) opóźnienia, wysyłając zapytanie na niektóre adresy URL i nagrywa, ile czasu to zajmie.
Po pierwsze, używam retrofit aby uzyskać odpowiedzi JSON z serwera sieci web. Ta odpowiedź zawiera: nazwę hosta (np. Ebay UK), URL-adres hosta (np. www.ebay.co.uk) i adres URL obrazu. Ja сопоставляю odpowiedź z mojej klasy danych, który wygląda w następujący sposób:
data class(
val name: String,
var url: String,
val icon: String,
var averagePing: Long = -1
)
adres url jest właściwością var, tak jak przed wykonaniem połączeń w celu uzyskania wartości opóźnienia muszę dodać https://, aby złożyć wniosek.
Ja robię to tak:
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) }
}
}
}
Pierwsza pętla for przygotowuje moje dane. Wiersz po pętli for wysyła dane zasilacza recycler, aby natychmiast wyświetlić nazwę hosta, adres URL i ikona (to wszystko działa, Tzn. mam pracy obserwator dla LiveData), na razie czekam na wartości opóźnienia.
Druga pętla for powoduje funkcję do obliczania wartości opóźnienia dla każdego hosta, a funkcja updateHostList() aktualizuje dane LiveData.
Oto jak wyglądają funkcje:
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
}
}
}
Wszystko to dzieje się w modelu prezentacji. Obecnie jestem aktualizuję listę, wysyłając pełna lista ponownie, kiedy zdradzam jedną właściwość jednego elementu listy, co wydaje mi się okropnym.
Moje pytanie: Jak mogę zaktualizować tylko jedną właściwość hosta i zmusić go automatycznie aktualizować interfejs użytkownika?
Z góry dziękuję
Edit: Mój obserwator wygląda tak:
viewModel.hostListLiveData.observe(this, Observer { adapter.updateData(it) })
I updateData() wygląda tak:
fun updateData(freshHostList: List<Host>) {
hostList.clear()
hostList.addAll(freshHostList)
notifyDataSetChanged()
}
@ArpitShukla, proponujesz, żeby u mnie było 2 opcje aktualizacji? jeden do wyświetlania początkowej listy, a drugi do aktualizacji elementu listy? Albo ja po prostu umieścił jak notifyDataSetChanged (), jak i notifyItemChanged() w updateData()?
Edit2: zmieniłem połączenie mojej funkcji, aby uczynić go asynchroniczna.