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 değişikliğe yanıt olarak işlemler gerçekleştirir. Bu bileşenler, daha iyi organize edilmiş ve genellikle daha hafif, bakımı daha kolay kodlar üretmenize yardımcı olur.
Yaygın bir yöntem, bağımlı bileşenlerin işlemlerini etkinliklerin ve parçaların yaşam döngüsü yöntemlerinde uygulamaktır. Ancak bu kalıp, kodun kötü bir şekilde düzenlenmesine ve hataların artmasına neden olur. 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 içine 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. Bu bileşenler, davranışlarını bir etkinliğin veya parçanın mevcut yaşam döngüsü durumuna göre otomatik olarak ayarlayabilir.
Android Framework'te tanımlanan uygulama bileşenlerinin çoğuna bağlı yaşam döngüleri vardır. Yaşam döngüleri, işletim sistemi veya işleminizde çalışan çerçeve kodu tarafından yönetilir. Android'in işleyişinin temelini oluştururlar ve uygulamanız bunlara uymalıdır. Aksi takdirde bellek sızıntıları, hatta uygulama kilitlenmeleri yaşanabilir.
Cihazın konumunu ekranda 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ı yaparsınız. Birden fazla bileşeni yönetmek, onStart() ve onStop gibi yaşam döngüsü yöntemlerine önemli miktarda kod yerleştirir ve bu yöntemlerin bakımını zorlaştırır.
Ayrıca, bileşenin etkinlik veya parça durdurulmadan önce başlatılacağı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 ve bileşenin gerektiğinden daha uzun süre etkin kalmasına yol açabilir.
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 çözmenize yardımcı olan sınıflar ve arayüzler sağlar.
Yaşam döngüsü
Lifecycle, bir bileşenin (etkinlik veya parça gibi) yaşam döngüsü durumuyla ilgili bilgileri barındıran 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ü olayları. Bu etkinlikler, etkinliklerdeki ve parçalardaki geri çağırma etkinlikleriyle eşlenir.
Eyalet
Lifecycle nesnesi tarafından izlenen bileşenin mevcut durumu.
Durumları bir grafiğin düğümleri, etkinlikleri ise bu düğümler arasındaki kenarlar olarak düşünebilirsiniz.
Bir sınıf, DefaultLifecycleObserver sınıfını uygulayarak ve onCreate, onStart gibi ilgili yöntemleri geçersiz kılarak bileşenin yaşam döngüsü durumunu izleyebilir. Ardından, Lifecycle sınıfının addObserver() yöntemini çağırıp gözlemcinizin bir örneğini ileterek gözlemci ekleyebilirsiniz. Aşağıdaki örnekte bu işlem gösterilmektedir:
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, sonraki bölümde açıklanan LifecycleOwner arayüzünü uygular.
LifecycleOwner
LifecycleOwner, sınıfın bir Lifecycle olduğunu belirten tek yöntemli bir arayüzdür. Sınıf tarafından uygulanması gereken bir yöntemi (getLifecycle) 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 ayrı sınıflardan soyutlar ve bunlarla çalışan bileşenler yazılmasına olanak tanır. Herhangi bir özel uygulama sınıfı, LifecycleOwner arayüzünü uygulayabilir.
DefaultLifecycleObserver uygulayan bileşenler, LifecycleOwner uygulayan bileşenlerle sorunsuz çalışır. Bunun nedeni, 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'yi uygulamasını sağlayabilir ve ardından onCreate() yönteminde etkinliğin Lifecycle ile başlatabiliriz. Bu sayede MyLocationListener sınıfı kendi kendine yeterli hale gelir. Yani yaşam döngüsü durumundaki değişikliklere tepki verme mantığı, etkinlik yerine MyLocationListener içinde tanımlanır. Her bir bileşenin kendi mantığını depolaması, etkinliklerin ve parçaların mantığının yönetilmesini 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();
}
});
}
}
Yaygın kullanım alanlarından biri, Lifecycle şu anda iyi durumda değilse belirli geri çağırmaların çağrılmasını önlemektir. Örneğin, geri çağırma, etkinlik durumu kaydedildikten sonra bir parça işlemi çalıştırırsa kilitlenmeye neden olur. Bu nedenle, bu geri çağırmayı hiçbir zaman ç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, yaşam döngüsünü tamamen algılayabilir. LocationListener'mizi başka bir etkinlik veya parçadan kullanmamız gerekirse başlatmamız yeterli olur. 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 istemcileriniz, istemci tarafında manuel yaşam döngüsü yönetimi olmadan bu bileşenleri kolayca entegre edebilir.
Özel LifecycleOwner uygulama
Destek Kitaplığı 26.1.0 ve sonraki sürümlerdeki parçalar ve etkinlikler, LifecycleOwner arayüzünü zaten uygular.
LifecycleOwner yapmak 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. Bunun için aşağıdaki kod örneğini inceleyebilirsiniz:
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şenler için en iyi uygulamalar
- Kullanıcı arayüzü denetleyicilerinizi (etkinlikler ve parçalar) mümkün olduğunca yalın tutun.
Kendi verilerini almaya çalışmamalı, bunun yerine
ViewModelkullanmalı ve değişiklikleri görünümlere yansıtmak için birLiveDatanesnesini 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'e geri bildirmek olduğu, veriye dayalı kullanıcı arayüzleri yazmaya çalışın. - Veri mantığınızı
ViewModelsı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 (örneğin, bir ağdan)ViewModel'ın sorumluluğunda değildir. Bunun yerineViewModel, verileri getirmek için uygun bileşeni çağırmalı ve ardından sonucu kullanıcı arayüzü denetleyicisine geri göndermelidir. - Görünümleriniz ile kullanıcı arayüzü denetleyicisi arasında temiz bir arayüz sağlamak için veri bağlamayı kullanın. Bu sayede görünümlerinizi daha açıklayıcı hale getirebilir ve etkinlikleriniz ile parçalarınızda yazmanız gereken güncelleme kodunu en aza indirebilirsiniz. Bu işlemi Java programlama dilinde yapmayı tercih ediyorsanız 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 işlemek için bir sunucu sınıfı oluşturabilirsiniz. Bu zahmetli bir iş olabilir ancak kullanıcı arayüzü bileşenlerinizi test etmeyi kolaylaştırabilir.
ViewModeliçindeViewveyaActivitybağlamına referans vermekten kaçının.ViewModel, etkinlikten daha uzun süre etkin kalırsa (yapılandırma değişiklikleri durumunda) etkinliğiniz sızdırılır ve çöp toplayıcı tarafından düzgün şekilde silinmez.- Uzun süren görevleri ve eşzamansız 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. Örnek olarak şunlar verilebilir:
- Yaklaşık ve ayrıntılı konum güncellemeleri arasında geçiş yapma Konum uygulamanız görünür durumdayken ayrıntılı konum güncellemelerini etkinleştirmek ve uygulama arka plandayken yaklaşık konum güncellemelerine geçmek için yaşam döngüsüne duyarlı bileşenleri kullanın.
LiveData, yaşam döngüsüne duyarlı bir bileşendir ve kullanıcınız konum değiştirdiğinde uygulamanızın kullanıcı arayüzünü otomatik olarak güncellemesine olanak tanır. - Video arabelleğe alma işlemini durdurma ve başlatma. Video arabelleğe almayı mümkün olan en kısa sürede başlatmak için yaşam döngüsüne duyarlı bileşenleri kullanın ancak uygulama tamamen başlatılana kadar oynatmayı erteleyin. 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 (akışını) etkinleştirmek 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 çizilebilir öğeleri duraklatmak ve uygulama ön plandayken çizilebilir öğeleri devam ettirmek için yaşam döngüsüne duyarlı bileşenleri kullanın.
Durdurma etkinliklerini işleme
Bir Lifecycle, bir AppCompatActivity veya Fragment'e ait olduğunda Lifecycle'nin durumu CREATED olarak değişir ve AppCompatActivity veya Fragment'nin onSaveInstanceState() yöntemi çağrıldığında ON_STOP etkinliği gönderilir.
Bir Fragment veya AppCompatActivity'ın 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, uygulama durum kaydedildikten sonra FragmentTransaction çalıştırırsa FragmentManager bir istisna oluşturur. Ayrıntılar için commit() sayfasını inceleyin.
LiveData, gözlemcinin ilişkili Lifecycle en az STARTED değilse gözlemcisini çağırmaktan kaçınarak bu uç durumu önler. Arka planda, gözlemcisini çağırmaya karar vermeden önce isAtLeast() yöntemini çağırır.
Maalesef AppCompatActivity'ın onStop() yöntemi onSaveInstanceState'dan sonra çağrılır. Bu da kullanıcı arayüzü durum değişikliklerine izin verilmeyen ancak Lifecycle'ın henüz CREATED durumuna taşınmadığı bir boşluk bırakır.
Bu sorunu önlemek için beta2 ve daha eski sürümlerdeki Lifecycle sınıfı, etkinliği göndermeden durumu CREATED olarak işaretler. Böylece, sistem tarafından onStop() çağrılana kadar etkinlik gönderilmese bile mevcut durumu kontrol eden tüm kodlar gerçek değeri alır.
Maalesef bu çözümün iki büyük sorunu var:
- API düzeyi 23 ve daha düşük sürümlerde Android sistemi, başka bir etkinlik tarafından kısmen kapsansa bile etkinliğin durumunu kaydeder. Başka bir deyişle, Android sistemi
onSaveInstanceState()yöntemini çağırır ancakonStopyöntemini çağırması gerekmez. Bu durum, gözlemcinin kullanıcı arayüzü durumu değiştirilemese bile yaşam döngüsünün hâlâ etkin olduğunu düşündüğü uzun bir aralık oluşturabilir. LiveDatasınıfına benzer bir davranış sergilemek isteyen tüm sınıflar,Lifecyclesürümbeta 2ve daha eski sürümler tarafından sağlanan geçici çözümü uygulamalıdır.
Ek kaynaklar
Yaşam döngüsüne duyarlı bileşenlerle yaşam döngülerini yönetme hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın.