coroutine, eşzamansız olarak çalışan kodu basitleştirmek için Android'de kullanabileceğiniz bir eşzamanlılık tasarım kalıbıdır. Kotlinler, 1.3 sürümünde Kotlin'e eklendi ve diğer dillerdeki yerleşik kavramlara dayanıyor.
Android'de eş yordamlar, aksi halde ana iş parçacığını engelleyebilecek ve uygulamanızın yanıt vermemesine neden olabilecek uzun süreli görevlerin yönetilmesine yardımcı olur. Eş yordam kullanan profesyonel geliştiricilerin% 50'sinden fazlası üretkenlikte artış olduğunu bildirmiştir. Bu bölümde, bu sorunları gidermek için Kotlin eş yordamlarını nasıl kullanabileceğiniz açıklanmaktadır. Böylece, daha net ve öz uygulama kodları yazabilirsiniz.
Özellikler
Coroutines, Android'de eşzamansız programlama için önerdiğimiz çözümdür. Dikkate değer özellikler arasında şunlar sayılabilir:
- Hafiftir: Eşliğin çalıştığı iş parçacığını engellemeyen askıya alma desteği nedeniyle tek bir iş parçacığında çok sayıda eş yordam çalıştırabilirsiniz. Askıya alma işlemi, birçok eşzamanlı işlemi desteklerken engelleme karşısında bellekten tasarruf sağlar.
- Daha az bellek sızıntısı: Belirli bir kapsam dahilindeki işlemleri çalıştırmak için yapılandırılmış eşzamanlılık kullanın.
- Yerleşik iptal desteği: İptal işlemi, çalışan eş yordam hiyerarşisinde otomatik olarak aktarılır.
- Jetpack entegrasyonu: Birçok Jetpack kitaplığı, tam eş yordam desteği sağlayan uzantılar içerir. Bazı kitaplıklar, yapılandırılmış eşzamanlılık için kullanabileceğiniz kendi eşleşen kapsamlarını da sağlar.
Örneklere genel bakış
Uygulama mimarisi kılavuzu'na bağlı olarak, bu konudaki örnekler bir ağ isteğinde bulunur ve sonucu ana iş parçacığına döndürür. Uygulama, burada sonucu kullanıcıya gösterebilir.
ViewModel
Mimari bileşeni, ağ isteğini tetiklemek için ana iş parçacığındaki depo katmanını çağırır. Bu kılavuzda, ana iş parçacığının engelini kaldırmak için eş yordamlar kullanan çeşitli çözümler yinelenir.
ViewModel
, doğrudan eş yordamlarla çalışan bir dizi KTX uzantısı içerir. Bu uzantılar lifecycle-viewmodel-ktx
kitaplığıdır ve bu kılavuzda kullanılmıştır.
Bağımlılık bilgileri
Android projenizde eş yordam kullanmak için uygulamanızın build.gradle
dosyasına aşağıdaki bağımlılığı ekleyin:
Modern
dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' }
Kotlin
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9") }
Arka plan iş parçacığında yürütme
Ana iş parçacığında bir ağ isteği yapılması, yanıt alana kadar beklemesine veya engellenmesine neden olur. İş parçacığı engellendiği için işletim sistemi onDraw()
'i çağıramaz. Bu durum, uygulamanızın donmasına ve muhtemelen Uygulama Yanıt Vermiyor (ANR) iletişim kutusuna yol açar. Daha iyi bir kullanıcı deneyimi için bu işlemi bir arka plan iş parçacığı üzerinde çalıştıralım.
Önce Repository
sınıfımıza göz atalım ve ağ isteğini nasıl yerine getirdiğini görelim:
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
class LoginRepository(private val responseParser: LoginResponseParser) {
private const val loginUrl = "https://example.com/login"
// Function that makes the network request, blocking the current thread
fun makeLoginRequest(
jsonBody: String
): Result<LoginResponse> {
val url = URL(loginUrl)
(url.openConnection() as? HttpURLConnection)?.run {
requestMethod = "POST"
setRequestProperty("Content-Type", "application/json; utf-8")
setRequestProperty("Accept", "application/json")
doOutput = true
outputStream.write(jsonBody.toByteArray())
return Result.Success(responseParser.parse(inputStream))
}
return Result.Error(Exception("Cannot open HttpURLConnection"))
}
}
makeLoginRequest
eşzamanlıdır ve çağrı yapan ileti dizisini engeller. Ağ isteği yanıtını modellemek için kendi Result
sınıfımız vardır.
ViewModel
, kullanıcı bir düğmeyi tıkladığında ağ isteğini tetikler:
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun login(username: String, token: String) {
val jsonBody = "{ username: \"$username\", token: \"$token\"}"
loginRepository.makeLoginRequest(jsonBody)
}
}
Önceki kodda LoginViewModel
, ağ isteğinde bulunurken kullanıcı arayüzü iş parçacığını engeller. Yürütmeyi ana iş parçacığının dışına taşımanın en basit çözümü, yeni bir eş yordam oluşturmak ve ağ isteğini bir G/Ç iş parçacığında yürütmektir:
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun login(username: String, token: String) {
// Create a new coroutine to move the execution off the UI thread
viewModelScope.launch(Dispatchers.IO) {
val jsonBody = "{ username: \"$username\", token: \"$token\"}"
loginRepository.makeLoginRequest(jsonBody)
}
}
}
login
fonksiyonundaki eş yordam kodunu ayrıştıralım:
viewModelScope
,ViewModel
KTX uzantılarına dahil edilen önceden tanımlanmış birCoroutineScope
değeridir. Tüm eş yordamların bir kapsam içinde çalışması gerektiğini unutmayın.CoroutineScope
, ilgili bir veya daha fazla eş çocuğu yönetir.launch
, eş yordam oluşturan ve işlev gövdesinin yürütülmesini ilgili görev dağıtıcıya gönderen bir işlevdir.Dispatchers.IO
, bu eş yordasının G/Ç işlemleri için ayrılmış bir iş parçacığında yürütülmesi gerektiğini belirtir.
login
işlevi şu şekilde yürütülür:
- Uygulama, ana iş parçacığındaki
View
katmanındanlogin
işlevini çağırır. launch
yeni bir eş yordam oluşturur ve ağ isteği bağımsız olarak G/Ç işlemleri için ayrılmış bir iş parçacığında yapılır.- Eş yordam çalışırken,
login
işlevi muhtemelen ağ isteği tamamlanmadan önce yürütmeye devam eder ve geri döner. Basit olması için ağ yanıtının şimdilik yoksayıldığını unutmayın.
Bu eş yordam viewModelScope
ile başlatıldığından ViewModel
kapsamında yürütülür. ViewModel
, kullanıcı ekrandan ayrıldığı için kaldırılırsa viewModelScope
otomatik olarak iptal edilir ve çalışan tüm eşler de iptal edilir.
Önceki örnekle ilgili sorunlardan biri, makeLoginRequest
çağrısı yapan her şeyin yürütmeyi ana iş parçacığının dışına taşımasını açıkça unutmasının gerekmesidir. Bu sorunu bizim için çözmek üzere Repository
üzerinde nasıl değişiklik yapabileceğimize bakalım.
Ana güvenlik için eş yordamlar kullan
Ana iş parçacığındaki kullanıcı arayüzü güncellemelerini engellemeyen bir işlevi main-güvenli olarak kabul ederiz. Ana iş parçacığından makeLoginRequest
çağrısı yapmak kullanıcı arayüzünü engelleyeceğinden makeLoginRequest
işlevi main güvenli değildir. Bir eş çocuğun yürütülmesini farklı bir iş parçacığına taşımak için eş yordam kitaplığındaki withContext()
işlevini kullanın:
class LoginRepository(...) {
...
suspend fun makeLoginRequest(
jsonBody: String
): Result<LoginResponse> {
// Move the execution of the coroutine to the I/O dispatcher
return withContext(Dispatchers.IO) {
// Blocking network request code
}
}
}
withContext(Dispatchers.IO)
, eş kotinin yürütülmesini bir I/O iş parçacığına taşıyarak çağrı işlevimizi "ana güvenli" hale getirir ve kullanıcı arayüzünün gerektiğinde güncellenmesini sağlar.
makeLoginRequest
etiketi de suspend
anahtar kelimesiyle işaretlenmiş. Bu anahtar kelime, Kotlin'in bir eş yordam içinden çağrılacak bir işlevi zorunlu kılma yöntemidir.
Aşağıdaki örnekte, eş yordam LoginViewModel
içinde oluşturulmuştur.
makeLoginRequest
yürütmeyi ana iş parçacığının dışına taşırken login
işlevindeki eş yordam artık ana iş parçacığında yürütülebilir:
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun login(username: String, token: String) {
// Create a new coroutine on the UI thread
viewModelScope.launch {
val jsonBody = "{ username: \"$username\", token: \"$token\"}"
// Make the network call and suspend execution until it finishes
val result = loginRepository.makeLoginRequest(jsonBody)
// Display result of the network request to the user
when (result) {
is Result.Success<LoginResponse> -> // Happy path
else -> // Show error in UI
}
}
}
}
makeLoginRequest
bir suspend
işlevi olduğundan ve tüm suspend
işlevlerinin bir eş yordamda yürütülmesi gerektiğinden eş yordanın burada hâlâ gerekli olduğunu unutmayın.
Bu kod, önceki login
örneğinden birkaç yönden farklıdır:
launch
,Dispatchers.IO
parametresi almaz.launch
hedefine birDispatcher
iletmediğinizdeviewModelScope
öğesinden başlatılan tüm eş yordamlar ana iş parçacığında çalışır.- Ağ isteğinin sonucu, başarılı veya başarısız kullanıcı arayüzünü görüntülemek için artık işlenmektedir.
Giriş işlevi artık şu şekilde çalışmaktadır:
- Uygulama, ana iş parçacığındaki
View
katmanındanlogin()
işlevini çağırır. launch
, ana iş parçacığında yeni bir eş yordam oluşturur ve eş yordam yürütmeye başlar.- Eşzamanlında,
loginRepository.makeLoginRequest()
çağrısı,makeLoginRequest()
içindekiwithContext
bloğunun çalışması bitene kadar eş yordasının daha fazla yürütülmesini askıya alır. withContext
bloğu sona erdiğindelogin()
içindeki eş yordam, ağ isteğinin sonucuyla ana iş parçacığında yürütmeye devam eder.
İstisnaları işleme
Repository
katmanının oluşturabileceği istisnaları yönetmek için Kotlin'in istisnalar için yerleşik desteğini kullanın.
Aşağıdaki örnekte bir try-catch
bloğu kullanılmıştır:
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun login(username: String, token: String) {
viewModelScope.launch {
val jsonBody = "{ username: \"$username\", token: \"$token\"}"
val result = try {
loginRepository.makeLoginRequest(jsonBody)
} catch(e: Exception) {
Result.Error(Exception("Network request failed"))
}
when (result) {
is Result.Success<LoginResponse> -> // Happy path
else -> // Show error in UI
}
}
}
}
Bu örnekte, makeLoginRequest()
çağrısı tarafından oluşturulan beklenmedik istisnalar, kullanıcı arayüzünde hata olarak ele alınmaktadır.
Ek eş yordam kaynakları
Android'de eş yordamlara daha ayrıntılı bir bakış için Kotlin eş yordamlarıyla uygulama performansını iyileştirme bölümüne bakın.
Daha fazla eş yordam kaynağı için aşağıdaki bağlantılara bakın:
- Corutine'lere genel bakış (JetBrains)
- Corutins rehberi (JetBrains)
- Kotlin eş yordamları ve akışı için ek kaynaklar