T özelliğini değiştirdiğimde LiveData<MutableList<T><T>> güncellemesi nasıl yapılır?

0

Soru

Bazı url'lere bir istekte bulunarak ve bunun ne kadar süreceğini kaydederek (sözde) gecikme değerlerini alan bir uygulama yapıyorum.

İlk olarak, bir web sunucusundan bir JSON yanıtı almak için güçlendirme kullanıyorum. Bu yanıt şunları içerir: ana bilgisayarın adı (ör. Ebay UK), ana bilgisayarın url'si (ör. www.ebay.co.uk) ve bir resim url'si. Bu yanıtı aşağıdaki gibi görünen veri sınıfımla eşleştiriyorum:

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

url bir var özelliğidir, çünkü gecikme değerlerini almak için arama yapmadan önce isteği yapmak için https:// eklemem gerekir.

Sanki bu her şeyi yapıyorum yani:

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) }
        }
    }
}

İlk for döngüsü verilerimi hazırlar. For döngüsünden sonraki satır, ana bilgisayar adını, url'yi ve simgeyi hemen göstermek için verileri geri dönüştürücü bağdaştırıcısına gönderir (tüm bunlar çalışır, yani LiveData için çalışan bir gözlemcim var), gecikme değerlerini beklerken.

İkinci for döngüsü, her ana bilgisayar için gecikme değerlerini hesaplama işlevini çağırır ve updateHostList () işlevi livedata'yı günceller.

İşlevler böyle görünür:

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
        }
    }
}

Bütün bunlar Viewmodel'de gerçekleşir. Şu anda listenin bir öğesinin bir özelliğini değiştirdiğimde tüm listeyi tekrar göndererek listemi güncelliyorum, ki bu bana korkunç geliyor.

Benim sorum Nasıl ev sahibi tek özelliği güncelleme ve otomatik olarak UI yenilemek zorunda miyim?

Şimdiden teşekkür ederiz

Düzenleme: Gözlemcim şuna benziyor:

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

Ve updateData() şöyle görünür:

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

@ArpitShukla, 2 güncelleme işlevine sahip olmamı öneriyor musunuz? biri ilk listeyi göstermek için diğeri de listenin öğesini güncellemek için mi? Yoksa hem notifyDataSetChanged() hem de notifyItemChanged() öğesini updateData () öğesine koyar mıyım?

Edıt2: işlev çağrımı zaman uyumsuz hale getirmek için değiştirdi.

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

En iyi cevabı

1

Gözlemlenen öğeleri güncellemeyi düşünebilirsiniz hostListLiveData kullanım notifyItemChanged(position) yerine notifyDataSetChanged() senin içinde adapter.

notifyItemChanged(position) yalnızca öğenin içeriğini güncelleyen bir öğe değiştirme olayıdır.

DÜZENLEMEK:
Kullanıyorsun notifyDataSetChanged() verilerin yeniden yayınlanmasına ve yeniden bağlanmasına neden olan içeriğin güncellenmesi üzerine RecyclerView ki bunu beklemiyorsun. Bu nedenle, verilerinizin içeriğini kullanarak güncellemelisiniz notifyItemChanged(position).

Güncellemeniz için yeni bir işlev oluşturabileceğinizi düşünüyorum. RecyclerView adaptörde örn.

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

ve gözlemcinizde, bunun yeni bir liste mi yoksa güncellenmiş bir liste mi olduğunu kontrol etmeniz gerekebilir

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

Bu işe yarıyor, ama bunun LiveData ile ilgili olduğunu sanmıyorum. Bunu test etmek için bir liste gösteren basit bir geri dönüşümcü görünümü yaptım ve bağdaştırıcıyı aradım.notifyItemChanged (konum). Bu işe yaradı ama LiveData ile nasıl ilişkili olduğunu anlamıyorum. Açıklığa kavuşturabilir misiniz, lütfen? Not: Gözlemcimin nasıl çalıştığını gösteren soruyu güncelleyeceğim, bunun size biraz daha bağlam vereceğini düşünüyorum
SpawnTheTronix

Evet, bu LiveData ile ilgili değil, güncelleme şeklinden kaynaklanıyor RecyclerView. Kullanıyorsunuz notifyDataSetChanged() verilerinizin içeriğinin güncellenmesi hakkında (örn. host ve ping). Bu notifyDataSetChanged() tüm görünür verileri tamamen yeniden bağlayacak ve yeniden yayınlayacaktır.
Putra Nugraha

Ben de kullanmayı denedim ListAdapter yerine RecyclerView.Adapter, benim de istenen işlevselliği elde etti. Neyin daha iyi olduğunu biliyor musun notifyDataSetChanged() ya ListAdapter? Bildiğim kadarıyla anlıyorum notifyDataSetChanged(), görünümü günceller (satır RecyclerView) güncellemesini söylersiniz.ListAdapter yeni listedeki ve eski listedeki farklılıkları denetler ve sonra değiştirilen alanı günceller (örn. TextView) yeni değere (yalnızca güncelleyip güncellemediğinden emin değilim TextView ya da tüm sıra, bu durumda hiçbir fark olmayacak mı?).
SpawnTheTronix

ListAdapter kaputun altında kullanıyor AsyncListDiffer depolanan veriler ile sağlanan veriler arasındaki farkların hesaplanmasına ve verilerin nasıl karşılaştırıldığına yardımcı olmak için, aşağıdaki tabloda tanımlanan koşul esas alınır: DiffUtil.ItemCallback. AFAIK, ListAdapter senin relayout olmaz RecyclerView ancak yalnızca değiştirilen verileri güncelleyin. İyi, ListAdapter yine de davanız için uygun bir çözümdür
Putra Nugraha

Diğer dillerde

Bu sayfa diğer dillerde

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