Mam pytanie w głowie i nie wiem, jak to sprawdzić.

Załóżmy, że mam funkcję getData, która ładuje dane z obiektu dziedziny i zajmuje to 5 sekund.

Bez coroutine muszę wywołać tę metodę w wątku w tle, aby uniknąć blokowania interfejsu użytkownika.

Jeśli zmienię getData na wstrzymanie funkcji i wywołam to w głównym wątku, czy nadal będzie zamrażać wątek interfejsu użytkownika na 5 sekund? A może będzie to jak wywołanie delay (5000) wewnątrz funkcji getData, czy nie zablokuje wątku interfejsu użytkownika?

Chcę utworzyć prosty projekt, aby to sprawdzić, ale nie mam wystarczająco dużych danych, aby spowodować opóźnienie o 5 sekund.

1
Arst 17 listopad 2019, 14:24
Chociaż jeśli uzyskanie danych z Realm zajmuje 5 sekund, to jestem ciekaw, ile masz danych. Realm nie lubi być przerzucany między wątkami, ponieważ jest ograniczony do wątków, ale zapytania podobno są dość leniwe.
 – 
EpicPandaForce
27 listopad 2019, 16:30

2 odpowiedzi

Jeśli zmienię funkcję getData na suspend i wywołam ją w wątku głównym, czy wątek interfejsu użytkownika nadal będzie blokowany na 5 sekund?

Tak, współprogramy nie mogą cofnąć faktu, że dany fragment kodu wykonuje wywołanie funkcji blokującej. Jest to typowy idiom, którego możesz użyć do przekazania wywołania blokującego do wątku w tle, a następnie kontynuowania pracy z nim w wątku interfejsu użytkownika:

myScope.launch {
    val result = withContext(Dispatchers.IO) {
        // blocking call here
    }
    // use the result to update the GUI here
}
2
Marko Topolnik 17 listopad 2019, 17:57

Myślę, że to zależy od tego, co masz na myśli

Jeśli zmienię getData, aby zawiesić funkcję

  • Jeśli po prostu dodasz słowo kluczowe suspend do funkcji, nic to nie zmieni, tj. wątek będzie blokowany do czasu otrzymania odpowiedzi.
  • Jeśli przepiszesz funkcję tak, aby była „zawieszona”, to będzie
    1. uruchom żądanie
    2. zawieś się (bez blokowania wątku)
    3. wznów po otrzymaniu odpowiedzi

Aby to osiągnąć, możesz użyć funkcji suspendCoroutine z biblioteki Coroutines.

Abstrakcyjnym przykładem może być coś takiego jak poniżej:

suspend fun getData(): String = suspendCoroutine { continuation ->

        // 1. Trigger event
        serverApi.getData().onResult(

            // 3. Resume either with data or error, after callback received from serverApi
            onSuccess = {
                continuation.resume("Data") 
            },
            onError = {
                continuation.resume("Error")
            } 
        )

        // 2. Suspend after block is finished
    }

PS Prawdopodobnie w twoim przypadku (pobranie danych z db) powyższe nie zadziała, ponieważ nie ma nic do zawieszenia (funkcja jest cały czas zajęta, w przeciwieństwie do żądań sieciowych).

1
Myroslav 8 styczeń 2020, 16:02