Android'de Kotlin eş yordamları

Eşzamanlı bir eş zamanlılık tasarım kalıbıdır. Bu yapıda Android'in eşzamansız olarak çalışan kodları basitleştirmesi. Koordinler 1.3 sürümünde Kotlin'e eklenmiştir ve diğer dillerdeki kavramlar olabilir.

Android'de eş yordamlar, gerçekleşebilecek uzun süreli görevlerin aksi takdirde ana iş parçacığını engeller ve uygulamanızın yanıt vermemesine neden olur. Eş yordam kullanan profesyonel geliştiricilerin% 50'den fazlası, verimlilik artışı sağlar. Bu bölümde, bu sorunları ele almak için Kotlin eş yordamlarını nasıl kullanabileceğiniz açıklanmaktadır. Böylece daha net ve kısa ve öz uygulama kodu yazabilirsiniz.

Özellikler

Eş yordamlar, Android Önemli özelliklerden bazıları şunlardır:

  • Hafif: şunun için destek: askıya alma, eş yordamın çalıştırıldığı iş parçacığını engellemez. Askıya alınıyor birçok eşzamanlı işlemi desteklerken engelleme sırasında bellek tasarrufu sağlar.
  • Daha az bellek sızıntısı: yapılandırılmış eşzamanlılık pek çok farklı yolu vardır.
  • Yerleşik iptal desteği: İptal çalışan eş hatlı hiyerarşide otomatik olarak yayılır.
  • Jetpack entegrasyonu: Çoğu Jetpack kitaplığı tam eş yordam desteği sağlayan uzantılar. Biraz kütüphaneler ayrıca kendi eşdeğer kapsam yapılandırılmış eş zamanlılık için de kullanılıyor.

Örneklere genel bakış

Uygulama mimarisi rehberi doğrultusunda bir ağ isteğinde bulunun ve sonucu ana makineye döndürün iş parçacığı (burada uygulama daha sonra sonucu kullanıcıya gösterebilir).

Daha ayrıntılı belirtmek gerekirse, ViewModel Mimari bileşeni, ana iş parçacığındaki depo katmanını ağ isteğini tetikler. Bu kılavuz, çeşitli çözümlerle aynıdır. eş yordamlar kullanan başka etiketler de oluşturabilirsiniz.

ViewModel, doğrudan eş yordamlar. Bu uzantılar lifecycle-viewmodel-ktx kitaplığı ve kullanılıyor inceleyebilirsiniz.

Bağımlılık bilgisi

Android projenizde eş yordamlar kullanmak için aşağıdaki bağımlılığı ekleyin (uygulamanızın build.gradle dosyasına):

Groovy

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
}

Kotlin

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
}

Bir arka plan iş parçacığında yürütülüyor

Ana iş parçacığında bir ağ isteğinde bulunmak, ağ isteğinin beklemesine veya engellenmesine neden olur. yanıt alana kadar devam eder. İleti dizisi engellendiği için onDraw() çağırabiliyor. Bu da uygulamanızın donmasına neden olabilir. Uygulama Yanıt Vermiyor (ANR) iletişim kutusuna yönlendirir. Daha iyi bir kullanıcı için bu işlemi bir arka plan iş parçacığı üzerinde çalıştıralım.

İlk olarak Repository dersimize bakalım ve nasıl bir deneyim olduğunu öğrenelim ağ isteğinde bulunun:

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ı ve görüşme dizisini engelliyor. Model oluşturmak için için kendi Result sınıfımızı oluşturuyoruz.

ViewModel, kullanıcı reklam öğesini tıkladığında ağ isteğini tetikler. bir düğme üzerinde:

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şağıdaki durumlarda kullanıcı arayüzü ileti dizisini engelliyor. . Yürütmeyi taşımak için en basit çözüm ana iş parçacığının dışında yeni bir eş yordam oluşturup ağı yürütmektir. bir G/Ç iş parçacığında istek:

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 fonksiyonunda eş yordam kodunu inceleyelim:

  • viewModelScope,CoroutineScope ViewModel KTX uzantısı var. Tüm eş yordamların kapsam. CoroutineScope bir veya daha fazla ilgili eş yordamı yönetir.
  • launch, eş yordam oluşturan ve ilgili sevk görevlisine iletmesini sağlamak.
  • Dispatchers.IO, bu eş yordamın iş parçacığı G/Ç işlemleri için ayrılmış.

login işlevi şu şekilde yürütülür:

  • Uygulama, ana iş parçacığındaki View katmanından login işlevini çağırır.
  • launch yeni bir eş yordam oluşturur ve ağ isteği yapılır için ayrılmış bir iş parçacığında bağımsız olarak oluşturulur.
  • Eş yordam çalışırken login işlevi yürütmeye devam eder büyük olasılıkla ağ isteği tamamlanmadan önce döner. Lütfen ağ yanıtı şimdilik yoksayılmaktadır.

Bu eş yordam viewModelScope ile başladığından ViewModel kapsamına girer. ViewModel kullanıcı ekrandan ayrılıyorsa viewModelScope otomatik olarak ve çalışan tüm eş yordamlar da iptal edilir.

Önceki örnekteki sorunlardan biri, makeLoginRequest, yürütmeyi açıkça devre dışı bırakmayı hatırlaması gerekir takip edebilirsiniz. Repository öğesini değiştirerek nasıl çözebileceğimize bakalım. çözmüştük.

Ana güvenlik için eş yordamlar kullan

main-safe, iş parçacığı. makeLoginRequest işlevi ana güvenli değil, çünkü çağrı Ana iş parçacığındaki makeLoginRequest, kullanıcı arayüzünü engelliyor. Şunu kullanın: Yürütmeyi taşımak için eş yordamlar kitaplığından withContext() işlevi farklı bir iş parçacığına dönüştürme:

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ş yordamın yürütülmesini bir I/O iş parçacığı, çağrı işlevimizi ana güvenli hale getirir ve kullanıcı arayüzünün gerektiği şekilde güncelleyin.

makeLoginRequest, suspend anahtar kelimesiyle de işaretlenmiş. Bu anahtar kelime Kotlin'in, 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 eş yordam, login işlevindeki öğeler 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 değeri olduğundan eş yordamın burada da gerekli olduğunu unutmayın bir suspend işlevi olmalı ve tüm suspend işlevleri eş yordam.

Bu kod, önceki login örneğinden birkaç açıdan farklıdır:

  • launch, Dispatchers.IO parametresini almaz. Bunu yaptığınızda launch konumuna Dispatcher aktarmak için, viewModelScope ana iş parçacığında çalışıyor.
  • Ağ isteğinin sonucu, başarıyı göstermek için artık işlenir. kullanıcı arayüzü.

Giriş işlevi artık şu şekilde yürütülmektedir:

  • Uygulama, ana iş parçacığındaki View katmanından login() işlevini çağırır.
  • launch, ana iş parçacığında yeni bir eş yordam oluşturur ve eş yordam başlar birkaç adım var.
  • Eş yordam içinde, loginRepository.makeLoginRequest() çağrısı artık eş yordamın yürütülmesi withContext tarihine kadar askıya alınıyor makeLoginRequest() içindeki bloğun çalışması tamamlanacak.
  • withContext bloku tamamlandığında login() içindeki eş yordam devam eder ağ isteğinin sonucuyla birlikte ana iş parçacığında yürütme.
ziyaret edin.

İstisnaları işleme

Repository katmanının atabileceği istisnaları işlemek için Kotlin katmanını kullanın istisnalar için yerleşik destek. Aşağıdaki örnekte 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() tarafından atılan beklenmedik bir istisna çağrısı, kullanıcı arayüzünde hata olarak işlenir.

Ek eş yordam kaynakları

Android'deki eş yordamlara daha ayrıntılı bir bakış için Kotlin eş yordamlarıyla uygulama performansını iyileştirin.

Diğer eş yordam kaynakları için aşağıdaki bağlantılara bakın: