Kotlin eş yordamları, eşzamansız kod yazmanıza olanak tanıyan bir API sağlar. Kotlin eş yordamlarıyla, eş yordamlarınızın ne zaman çalışması gerektiğini yönetmenize yardımcı olan bir CoroutineScope
tanımlayabilirsiniz. Her eşzamansız işlem, belirli bir kapsam dahilinde çalışır.
Yaşam döngüsüne duyarlı bileşenler, LiveData
ile birlikte çalışabilirlik katmanının yanı sıra uygulamanızdaki mantıksal kapsamlar için eş yordamlar için birinci sınıf destek sağlar.
Bu konuda, eş yordamların yaşam döngüsüne duyarlı bileşenlerle etkili bir şekilde nasıl kullanılacağı açıklanmaktadır.
KTX bağımlılıkları ekleyin
Bu konuda açıklanan yerleşik eş yordam kapsamları, karşılık gelen her bileşen için KTX uzantılarında yer almaktadır. Bu kapsamları kullanırken uygun bağımlılıkları eklediğinizden emin olun.
ViewModelScope
içinandroidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0
veya üstünü kullanın.LifecycleScope
içinandroidx.lifecycle:lifecycle-runtime-ktx:2.4.0
veya üstünü kullanın.liveData
içinandroidx.lifecycle:lifecycle-livedata-ktx:2.4.0
veya üstünü kullanın.
Yaşam döngüsüne duyarlı eş yordam kapsamları
Yaşam döngüsüne duyarlı bileşenler, uygulamanızda kullanabileceğiniz aşağıdaki yerleşik kapsamları tanımlar.
ModelKapsamı Görüntüleme
Uygulamanızdaki her ViewModel
için bir ViewModelScope
tanımlanır. Bu kapsamda başlatılan tüm eş yordamlar, ViewModel
temizlenirse otomatik olarak iptal edilir. Eşler burada, yalnızca ViewModel
etkinse yapılması gereken işler için kullanışlıdır. Örneğin, bir düzen için bazı verileri hesaplıyorsanız işin kapsamını ViewModel
öğesine ayarlamanız gerekir. Böylece, ViewModel
temizlenirse kaynak tüketiminin önüne geçmek için iş otomatik olarak iptal edilir.
Aşağıdaki örnekte gösterildiği gibi, bir ViewModel
öğesinin CoroutineScope
öğesine ViewModel'in viewModelScope
özelliği aracılığıyla erişebilirsiniz:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
Yaşam DöngüsüKapsamı
Her bir Lifecycle
nesnesi için bir LifecycleScope
tanımlanır. Bu kapsamda başlatılan tüm eş yordamlar, Lifecycle
kaldırıldığında iptal edilir. Lifecycle
öğesinin CoroutineScope
öğesine lifecycle.coroutineScope
veya lifecycleOwner.lifecycleScope
mülkleri aracılığıyla erişebilirsiniz.
Aşağıdaki örnekte, önceden hesaplanmış metni eşzamansız olarak oluşturmak için lifecycleOwner.lifecycleScope
özelliğinin nasıl kullanılacağı gösterilmektedir:
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(longTextContent, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
}
Yeniden başlatılabilir Yaşam Döngüsüne duyarlı eş yordamlar
lifecycleScope
, Lifecycle
DESTROYED
olduğunda uzun süreli işlemleri otomatik olarak iptal etmek için uygun bir yol sağlasa da, Lifecycle
belirli bir durumda olduğunda kod bloğunun yürütülmesini başlatmak ve başka bir durumda iptal etmek isteyebileceğiniz başka durumlarınız olabilir. Örneğin, Lifecycle
STARTED
olduğunda bir akış toplamak ve STOPPED
olduğunda koleksiyonu iptal etmek isteyebilirsiniz. Bu yaklaşım, akış emisyonlarını yalnızca kullanıcı arayüzü ekranda göründüğünde işleyerek kaynaklardan tasarruf sağlar ve potansiyel olarak uygulama kilitlenmelerini önler.
Bu durumlarda Lifecycle
ve LifecycleOwner
, tam olarak bunu yapan askıya alma repeatOnLifecycle
API'sini sağlar. Aşağıdaki örnek, ilişkilendirilen Lifecycle
en az STARTED
durumunda olduğunda her çalışan ve Lifecycle
STOPPED
olduğunda iptal edilen bir kod bloğu içerir:
class MyFragment : Fragment() {
val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Create a new coroutine in the lifecycleScope
viewLifecycleOwner.lifecycleScope.launch {
// repeatOnLifecycle launches the block in a new coroutine every time the
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// Trigger the flow and start listening for values.
// This happens when lifecycle is STARTED and stops
// collecting when the lifecycle is STOPPED
viewModel.someDataFlow.collect {
// Process item
}
}
}
}
}
Yaşam döngüsüne duyarlı akış toplama
Yalnızca tek bir akışta yaşam döngüsüne duyarlı toplama işlemi gerçekleştirmeniz gerekiyorsa kodunuzu basitleştirmek için Flow.flowWithLifecycle()
yöntemini kullanabilirsiniz:
viewLifecycleOwner.lifecycleScope.launch {
exampleProvider.exampleFlow()
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collect {
// Process the value.
}
}
Ancak paralel olarak birden çok akışta yaşam döngüsüne duyarlı toplama işlemi gerçekleştirmeniz gerekiyorsa her bir akışı farklı eş yordamlarda toplamanız gerekir. Bu durumda, doğrudan repeatOnLifecycle()
kullanmak daha verimli olur:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// Because collect is a suspend function, if you want to
// collect multiple flows in parallel, you need to do so in
// different coroutines.
launch {
flow1.collect { /* Process the value. */ }
}
launch {
flow2.collect { /* Process the value. */ }
}
}
}
Yaşam döngüsüne duyarlı eş yordamları askıya alma
CoroutineScope
, uzun süreli işlemleri otomatik olarak iptal etmek için uygun bir yol sağlasa da, Lifecycle
belirli bir durumda değilse kod bloğunun yürütülmesini askıya almak isteyebileceğiniz başka durumlarınız olabilir. Örneğin, bir FragmentTransaction
çalıştırmak için Lifecycle
en az STARTED
olana kadar beklemeniz gerekir. Lifecycle
, bu durumlarda ek yöntemler sunar: lifecycle.whenCreated
, lifecycle.whenStarted
ve lifecycle.whenResumed
. Lifecycle
en azından istenen minimum durumda değilse bu blokların içinde çalışan tüm eş yordam askıya alınır.
Aşağıdaki örnek, yalnızca ilişkilendirilen Lifecycle
en az STARTED
durumunda olduğunda çalışan bir kod bloğu içerir:
class MyFragment: Fragment {
init { // Notice that we can safely launch in the constructor of the Fragment.
lifecycleScope.launch {
whenStarted {
// The block inside will run only when Lifecycle is at least STARTED.
// It will start executing when fragment is started and
// can call other suspend methods.
loadingView.visibility = View.VISIBLE
val canAccess = withContext(Dispatchers.IO) {
checkUserAccess()
}
// When checkUserAccess returns, the next line is automatically
// suspended if the Lifecycle is not *at least* STARTED.
// We could safely run fragment transactions because we know the
// code won't run unless the lifecycle is at least STARTED.
loadingView.visibility = View.GONE
if (canAccess == false) {
findNavController().popBackStack()
} else {
showContent()
}
}
// This line runs only after the whenStarted block above has completed.
}
}
}
Bir eş yordam, when
yöntemlerinden biri kullanılarak etkinken Lifecycle
kaldırılırsa eş yorda otomatik olarak iptal edilir. Aşağıdaki örnekte, finally
bloğu Lifecycle
durumu DESTROYED
olduğunda çalıştırılır:
class MyFragment: Fragment {
init {
lifecycleScope.launchWhenStarted {
try {
// Call some suspend functions.
} finally {
// This line might execute after Lifecycle is DESTROYED.
if (lifecycle.state >= STARTED) {
// Here, since we've checked, it is safe to run any
// Fragment transactions.
}
}
}
}
}
LiveData ile eş yordamları kullanma
LiveData
kullanırken değerleri eşzamansız olarak hesaplamanız gerekebilir. Örneğin, bir kullanıcının tercihlerini alıp
kullanıcı arayüzünüze sunmak isteyebilirsiniz. Bu durumlarda, liveData
oluşturucu işlevini kullanarak bir suspend
işlevini çağırabilir ve sonucu LiveData
nesnesi olarak sunabilirsiniz.
Aşağıdaki örnekte loadUser()
, başka bir yerde tanımlanan bir askıya alma işlevidir. loadUser()
öğesini eşzamansız olarak çağırmak için liveData
derleyici işlevini, ardından sonucu ortaya çıkarmak için emit()
işlevini kullanın:
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
liveData
yapı taşı, eş yordamlar ve LiveData
arasında yapılandırılmış bir eş zamanlı temel öğe görevi görür. Kod bloğu, LiveData
etkin hale geldiğinde yürütülmeye başlar ve LiveData
etkin olmadığında, yapılandırılabilir bir zaman aşımının ardından otomatik olarak iptal edilir. İşlem tamamlanmadan önce iptal edilirse LiveData
tekrar etkin hale gelirse yeniden başlatılır. Önceki bir çalıştırmada başarılı bir şekilde tamamlanırsa yeniden başlatılmaz. Yalnızca otomatik olarak iptal edildiğinde yeniden başlatıldığını unutmayın. Engelleme başka bir nedenden dolayı (ör. CancellationException
gönderilmesi) iptal edilirse engelleme yeniden başlatılmaz.
Ayrıca, bloktan birden çok değer de yayınlayabilirsiniz. Her emit()
çağrısı, ana iş parçacığında LiveData
değeri ayarlanana kadar engellemenin yürütülmesini askıya alır.
val user: LiveData<Result> = liveData {
emit(Result.loading())
try {
emit(Result.success(fetchUser()))
} catch(ioException: Exception) {
emit(Result.error(ioException))
}
}
Aşağıdaki örnekte gösterildiği gibi liveData
ile Transformations
kodunu da birleştirebilirsiniz:
class MyViewModel: ViewModel() {
private val userId: LiveData<String> = MutableLiveData()
val user = userId.switchMap { id ->
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
emit(database.loadUserById(id))
}
}
}
Yeni bir değer yayınlamak istediğinizde emitSource()
işlevini çağırarak bir LiveData
öğesinden birden fazla değer yayınlayabilirsiniz. emit()
veya emitSource()
çağrılarının önceden eklenen kaynağı kaldırdığını unutmayın.
class UserDao: Dao {
@Query("SELECT * FROM User WHERE id = :id")
fun getUser(id: String): LiveData<User>
}
class MyRepository {
fun getUser(id: String) = liveData<User> {
val disposable = emitSource(
userDao.getUser(id).map {
Result.loading(it)
}
)
try {
val user = webservice.fetchUser(id)
// Stop the previous emission to avoid dispatching the updated user
// as `loading`.
disposable.dispose()
// Update the database.
userDao.insert(user)
// Re-establish the emission with success type.
emitSource(
userDao.getUser(id).map {
Result.success(it)
}
)
} catch(exception: IOException) {
// Any call to `emit` disposes the previous one automatically so we don't
// need to dispose it here as we didn't get an updated value.
emitSource(
userDao.getUser(id).map {
Result.error(exception, it)
}
)
}
}
}
Eş yordamlarla ilgili daha fazla bilgi için aşağıdaki bağlantılara bakın:
- Kotlin eş yordamlarıyla uygulama performansını iyileştirme
- Koritlere genel bakış
- CoroutineWorker'da ileti dizisi oluşturma
Ek kaynaklar
Yaşam döngüsüne duyarlı bileşenlere sahip eş yordamları kullanma hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara başvurun.
Sana Özel
Bloglar
- Android'de Koretinler: Uygulama kalıpları
- Android'de kolay eş yordamlar: viewModelScope
- Eşzamanlılarda art arda iki LiveData emisyonunu test etme
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken gösterilir
- LiveData'ya genel bakış
- Yaşam Döngüsüne Duyarlı Bileşenlerle Yaşam Döngülerini Yönetme
- Sayfalı verileri yükleme ve görüntüleme