Yaşam döngüsüne duyarlı bileşenlerle (Görünümler) yaşam döngülerini işleme

Kavramlar ve Jetpack Compose uygulaması

Yaşam döngüsüne duyarlı bileşenler, etkinlikler ve parçalar gibi başka bir bileşenin yaşam döngüsü durumundaki bir değişikliğe yanıt olarak işlemler gerçekleştirir. Bu bileşenler, daha iyi düzenlenmiş ve genellikle daha hafif olan, bakımı daha kolay kodlar üretmenize yardımcı olur.

Bağımlı bileşenlerin işlemlerini etkinliklerin ve parçaların yaşam döngüsü yöntemlerinde uygulamak yaygın bir yöntemdir. Ancak bu kalıp, kodun kötü düzenlenmesine ve hataların yaygınlaşmasına yol açar. Yaşam döngüsüne duyarlı bileşenleri kullanarak bağımlı bileşenlerin kodunu yaşam döngüsü yöntemlerinden çıkarıp bileşenlerin kendisine taşıyabilirsiniz.

androidx.lifecycle paketi, yaşam döngüsüne duyarlı bileşenler oluşturmanıza olanak tanıyan sınıflar ve arayüzler sağlar. Yaşam döngüsüne duyarlı bileşenler, davranışlarını bir etkinliğin veya parçanın mevcut yaşam döngüsü durumuna göre otomatik olarak ayarlayabilen bileşenlerdir.

Android Framework'te tanımlanan uygulama bileşenlerinin çoğuna yaşam döngüleri eklenmiştir. Yaşam döngüleri, işletim sistemi veya işleminizde çalışan çerçeve kodu tarafından yönetilir. Bunlar, Android'in işleyiş şeklinin temelini oluşturur ve uygulamanız bunlara uymalıdır. Aksi takdirde bellek sızıntıları veya uygulama kilitlenmeleri tetiklenebilir.

Ekranda cihaz konumunu gösteren bir etkinliğimiz olduğunu varsayalım. Yaygın bir uygulama aşağıdaki gibi olabilir:

Kotlin

internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) {

    fun start() {
        // connect to system location service
    }

    fun stop() {
        // disconnect from system location service
    }
}

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        myLocationListener.start()
        // manage other components that need to respond
        // to the activity lifecycle
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

Java

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

Bu örnek iyi görünse de gerçek bir uygulamada, yaşam döngüsünün mevcut durumuna yanıt olarak kullanıcı arayüzünü ve diğer bileşenleri yöneten çok fazla çağrı olur. Birden fazla bileşeni yönetmek, onStart() ve onStop gibi yaşam döngüsü yöntemlerine önemli miktarda kod yerleştirir. Bu da kodların bakımını zorlaştırır.

Ayrıca, bileşenin etkinlik veya parça durdurulmadan önce başlayacağının garantisi yoktur. Bu durum, özellikle onStart'da bazı yapılandırma kontrolleri gibi uzun süreli bir işlem yapmamız gerektiğinde geçerlidir. Bu durum, onStop() yönteminin onStart yönteminden önce tamamlandığı bir yarışma durumuna neden olabilir. Bu durumda bileşen, gerekenden daha uzun süre etkin kalır.

Kotlin

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        Util.checkUserStatus { result ->
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start()
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
    }

}

Java

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

androidx.lifecycle paketi, bu sorunları esnek ve izole bir şekilde ele almanıza yardımcı olan sınıflar ve arayüzler sağlar.

Yaşam döngüsü

Lifecycle, bir bileşenin (ör. etkinlik veya parça) yaşam döngüsü durumuyla ilgili bilgileri içeren ve diğer nesnelerin bu durumu gözlemlemesine olanak tanıyan bir sınıftır.

Lifecycle, ilişkili bileşeninin yaşam döngüsü durumunu izlemek için iki ana numaralandırma kullanır:

Etkinlik

Çerçeveden ve Lifecycle sınıfından gönderilen yaşam döngüsü etkinlikleri. Bu etkinlikler, etkinlikler ve parçalardaki geri çağırma etkinlikleriyle eşlenir.

Eyalet

Lifecycle nesnesi tarafından izlenen bileşenin mevcut durumu.

Şekil 1. Android etkinlik yaşam döngüsünün durumları ve olayları.

Durumları bir grafiğin düğümleri, etkinlikleri ise bu düğümler arasındaki kenarlar olarak düşünebilirsiniz.

Bir sınıf, DefaultLifecycleObserver uygulayarak ve onCreate, onStart gibi ilgili yöntemleri geçersiz kılarak bileşenin yaşam döngüsü durumunu izleyebilir. Ardından, aşağıdaki örnekte gösterildiği gibi, Lifecycle sınıfının addObserver() yöntemini çağırıp gözlemcinizin bir örneğini ileterek bir gözlemci ekleyebilirsiniz:

Kotlin

class MyObserver : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        connect()
    }

    override fun onPause(owner: LifecycleOwner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(MyObserver())

Java

public class MyObserver implements DefaultLifecycleObserver {
    @Override
    public void onResume(LifecycleOwner owner) {
        connect()
    }

    @Override
    public void onPause(LifecycleOwner owner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

Yukarıdaki örnekte, myLifecycleOwner nesnesi, aşağıdaki bölümde açıklanan LifecycleOwner arayüzünü uygular.

LifecycleOwner

LifecycleOwner, sınıfın Lifecycle içerdiğini belirten tek bir yöntem arayüzüdür. Sınıf tarafından uygulanması gereken getLifecycle adlı bir yöntemi vardır. Bunun yerine tüm uygulama sürecinin yaşam döngüsünü yönetmeye çalışıyorsanız ProcessLifecycleOwner başlıklı makaleyi inceleyin.

Bu arayüz, Lifecycle sahipliğini Fragment ve AppCompatActivity gibi sınıflardan soyutlar ve bunlarla çalışan bileşenlerin yazılmasına olanak tanır. Herhangi bir özel uygulama sınıfı LifecycleOwner arayüzünü uygulayabilir.

DefaultLifecycleObserver'yi uygulayan bileşenler, LifecycleOwner'yi uygulayan bileşenlerle sorunsuz bir şekilde çalışır. Bunun nedeni, bir sahibin gözlemcinin izlemek için kaydolabileceği bir yaşam döngüsü sağlayabilmesidir.

Konum izleme örneğinde, MyLocationListener sınıfının DefaultLifecycleObserver uygulamasını sağlayabilir ve ardından onCreate() yönteminde etkinliğin Lifecycle ile başlatabiliriz. Bu, MyLocationListener sınıfının kendi kendine yeterli olmasını sağlar. Yani yaşam döngüsü durumundaki değişikliklere tepki verme mantığı, etkinlik yerine MyLocationListener içinde tanımlanır. Ayrı bileşenlerin kendi mantıklarını depolaması, etkinlik ve parça mantığını yönetmeyi kolaylaştırır.

Kotlin

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this, lifecycle) { location ->
            // update UI
        }
        Util.checkUserStatus { result ->
            if (result) {
                myLocationListener.enable()
            }
        }
    }
}

Java

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}

Sık karşılaşılan bir kullanım alanı, Lifecycle şu anda iyi durumda değilse belirli geri çağırmaların çağrılmasını önlemektir. Örneğin, geri çağırma işlemi, etkinlik durumu kaydedildikten sonra bir parça işlemi çalıştırırsa kilitlenmeye neden olur. Bu nedenle, bu geri çağırma işlemini asla çağırmak istemeyiz.

Bu kullanım alanını kolaylaştırmak için Lifecycle sınıfı, diğer nesnelerin mevcut durumu sorgulamasına olanak tanır.

Kotlin

internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver {

    private var enabled = false

    override fun onStart(owner: LifecycleOwner) {
        if (enabled) {
            // connect
        }
    }

    fun enable() {
        enabled = true
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            // connect if not connected
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        // disconnect if connected
    }
}

Java

class MyLocationListener implements DefaultLifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @Override
    public void onStart(LifecycleOwner owner) {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @Override
    public void onStop(LifecycleOwner owner) {
        // disconnect if connected
    }
}

Bu uygulama sayesinde LocationListener sınıfımız tamamen yaşam döngüsüne duyarlı hale gelir. Başka bir etkinlikten veya parçadan LocationListener kullanmamız gerekiyorsa başlatmamız yeterlidir. Tüm kurulum ve kaldırma işlemleri sınıfın kendisi tarafından yönetilir.

Bir kitaplık, Android yaşam döngüsüyle çalışması gereken sınıflar sağlıyorsa yaşam döngüsüne duyarlı bileşenler kullanmanızı öneririz. Kitaplık müşterileriniz, bu bileşenleri istemci tarafında manuel yaşam döngüsü yönetimi olmadan kolayca entegre edebilir.

Özel bir LifecycleOwner uygulama

Destek Kitaplığı 26.1.0 ve sonraki sürümlerindeki Fragment'lar ve Activity'ler, LifecycleOwner arayüzünü zaten uygular.

LifecycleOwner oluşturmak istediğiniz özel bir sınıfınız varsa LifecycleRegistry sınıfını kullanabilirsiniz ancak etkinlikleri bu sınıfa yönlendirmeniz gerekir. Aşağıdaki kod örneğinde bu işlem gösterilmektedir:

Kotlin

class MyActivity : Activity(), LifecycleOwner {

    private lateinit var lifecycleRegistry: LifecycleRegistry

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }

    public override fun onStart() {
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    }

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

Java

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

Yaşam döngüsüne duyarlı bileşenlerle ilgili en iyi uygulamalar

  • Kullanıcı arayüzü denetleyicilerinizi (etkinlikler ve parçalar) mümkün olduğunca yalın tutun. Kendi verilerini edinmeye çalışmamalı, bunun yerine ViewModel kullanarak bunu yapmalı ve değişiklikleri görünümlere yansıtmak için bir LiveData nesnesi gözlemlemelidir.
  • Kullanıcı arayüzü denetleyicinizin sorumluluğunun, veriler değiştikçe görünümleri güncellemek veya kullanıcı işlemlerini ViewModel'ye geri bildirmek olduğu veriye dayalı kullanıcı arayüzleri yazmaya çalışın.
  • Veri mantığınızı ViewModel sınıfınıza yerleştirin. ViewModel, kullanıcı arayüzü denetleyiciniz ile uygulamanızın geri kalanı arasında bağlayıcı görevi görmelidir. Ancak dikkatli olun. Veri getirmek (ör. ağdan) ViewModel'ın sorumluluğunda değildir. Bunun yerine, ViewModel, verileri getirmek için uygun bileşeni çağırmalı ve ardından sonucu kullanıcı arayüzü denetleyicisine geri vermelidir.
  • Görünümleriniz ile kullanıcı arayüzü denetleyicisi arasında temiz bir arayüz sağlamak için Veri Bağlama'yı kullanın. Bu sayede görünümlerinizi daha bildirimsel hale getirebilir ve etkinliklerinizde ve parçalarınızda yazmanız gereken güncelleme kodunu en aza indirebilirsiniz. Bu işlemi Java programlama dilinde yapmayı tercih ederseniz ortak metin kodundan kaçınmak ve daha iyi bir soyutlama elde etmek için Butter Knife gibi bir kitaplık kullanın.
  • Kullanıcı arayüzünüz karmaşıksa kullanıcı arayüzü değişikliklerini yönetmek için bir sunucu sınıfı oluşturmayı düşünebilirsiniz. Bu zahmetli bir görev olsa da kullanıcı arayüzü bileşenlerinizin test edilmesini kolaylaştırabilir.
  • View veya Activity bağlamına ViewModel içinde referans vermeyin. ViewModel, etkinlikten daha uzun süre yaşarsa (yapılandırma değişiklikleri durumunda) etkinliğiniz sızar ve çöp toplayıcı tarafından uygun şekilde atılmaz.
  • Uzun süren görevleri ve eşzamanlı olarak çalıştırılabilen diğer işlemleri yönetmek için Kotlin eş yordamlarını kullanın.

Yaşam döngüsüne duyarlı bileşenlerin kullanım alanları

Yaşam döngüsüne duyarlı bileşenler, çeşitli durumlarda yaşam döngülerini yönetmenizi çok daha kolay hale getirebilir. Birkaç örnek:

  • Kaba ve ayrıntılı konum güncellemeleri arasında geçiş yapma Konum uygulamanız görünürken ayrıntılı konum güncellemelerini etkinleştirmek için yaşam döngüsüne duyarlı bileşenleri kullanın ve uygulama arka planda olduğunda kaba konum güncellemelerine geçin. Yaşam döngüsüne duyarlı bir bileşen olan LiveData, kullanıcınız konum değiştirdiğinde uygulamanızın kullanıcı arayüzünü otomatik olarak güncellemesini sağlar.
  • Video arabelleğe almayı durdurma ve başlatma Video arabelleğe almayı mümkün olan en kısa sürede başlatmak, ancak uygulama tamamen başlatılana kadar oynatmayı ertelemek için yaşam döngüsüne duyarlı bileşenler kullanın. Uygulamanız yok edildiğinde arabelleğe almayı sonlandırmak için yaşam döngüsüne duyarlı bileşenleri de kullanabilirsiniz.
  • Ağ bağlantısını başlatma ve durdurma Uygulama ön plandayken ağ verilerinin canlı olarak güncellenmesini (aktarılmasını) sağlamak ve uygulama arka plana geçtiğinde otomatik olarak duraklatmak için yaşam döngüsüne duyarlı bileşenleri kullanın.
  • Animasyonlu çizilebilir öğeleri duraklatma ve devam ettirme. Uygulama arka plandayken animasyonlu çekilebilir kaynakları duraklatmak ve uygulama ön plana geldiğinde çekilebilir kaynakları devam ettirmek için yaşam döngüsüne duyarlı bileşenleri kullanın.

Durdurma etkinliklerini işleme

Bir Lifecycle, AppCompatActivity veya Fragment'a ait olduğunda Lifecycle'nin durumu CREATED olarak değişir ve AppCompatActivity veya Fragment'ün onSaveInstanceState() çağrıldığında ON_STOP etkinliği gönderilir.

Fragment veya AppCompatActivity durumu onSaveInstanceState kullanılarak kaydedildiğinde, ON_START çağrılana kadar kullanıcı arayüzü değişmez olarak kabul edilir. Durum kaydedildikten sonra kullanıcı arayüzünü değiştirmeye çalışmak, uygulamanızın gezinme durumunda tutarsızlıklara neden olabilir. Bu nedenle, durum kaydedildikten sonra uygulama FragmentManager çalıştırırsa FragmentTransaction istisna oluşturur. Ayrıntılar için commit() başlıklı makaleyi inceleyin.

LiveData, gözlemcinin ilişkili Lifecycle değeri en az STARTED değilse gözlemcisini çağırmayarak bu uç durumu önler. Arka planda, gözlemcisini çağırmaya karar vermeden önce isAtLeast() işlevini çağırır.

Maalesef AppCompatActivity's onStop() yöntemi, onSaveInstanceState sonra çağrılıyor. Bu durum, kullanıcı arayüzü durumu değişikliklerine izin verilmeyen ancak Lifecycle öğesinin henüz CREATED durumuna taşınmadığı bir boşluk bırakıyor.

Bu sorunu önlemek için Lifecycle sınıfı, beta2 ve önceki sürümlerde durumu CREATED olarak işaretler. Böylece, etkinlik onStop() sistemi tarafından çağrılana kadar gönderilmese bile mevcut durumu kontrol eden tüm kodlar gerçek değeri alır.

Ancak bu çözümün iki büyük sorunu vardır:

  • API düzeyi 23 ve önceki sürümlerde Android sistemi, başka bir etkinlik tarafından kısmen kapsanmış olsa bile etkinliğin durumunu kaydeder. Başka bir deyişle, Android sistemi onSaveInstanceState() işlevini çağırır ancak onStop işlevini çağırması zorunlu değildir. Bu durum, gözlemcinin yaşam döngüsünün etkin olduğunu düşünmeye devam ettiği, ancak kullanıcı arayüzü durumunun değiştirilemediği uzun bir aralığa yol açabilir.
  • LiveData sınıfına benzer bir davranış sergilemek isteyen tüm sınıflar, Lifecycle sürümü ve daha düşük sürümler tarafından sağlanan geçici çözümü uygulamalıdır. beta 2

Ek kaynaklar

Yaşam döngüsüne duyarlı bileşenlerle yaşam döngülerini işleme hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın.

Örnekler

  • Sunflower: Mimari Bileşenler ile ilgili en iyi uygulamaları gösteren bir demo uygulaması

Codelab uygulamaları

Bloglar