Kullanıcı arayüzü durumlarını (görünümler) kaydetme

Kavramlar ve Jetpack Compose uygulaması

Bu kılavuzda, kullanıcıların kullanıcı arayüzü durumuyla ilgili beklentileri ve durumu korumak için kullanılabilecek seçenekler ele alınmaktadır.

Sistemin etkinlikleri veya uygulamaları yok etmesinden sonra bir etkinliğin kullanıcı arayüzü durumunu hızlı bir şekilde kaydetmek ve geri yüklemek, iyi bir kullanıcı deneyimi için çok önemlidir. Kullanıcılar, kullanıcı arayüzü durumunun aynı kalmasını bekler ancak sistem, etkinliği ve depolanmış durumunu yok edebilir.

Kullanıcı beklentileri ile sistem davranışı arasındaki farkı kapatmak için aşağıdaki yöntemlerin bir kombinasyonunu kullanın:

  • ViewModel nesneler.
  • Aşağıdaki bağlamlardaki kaydedilmiş örnek durumları:
  • Uygulama ve etkinlik geçişleri sırasında kullanıcı arayüzü durumunu kalıcı hale getirmek için yerel depolama alanı.

En uygun çözüm, kullanıcı arayüzü verilerinizin karmaşıklığına, uygulamanızın kullanım alanlarına ve veri erişim hızı ile bellek kullanımı arasında bir denge kurmaya bağlıdır.

Uygulamanızın kullanıcıların beklentilerini karşıladığından ve hızlı, duyarlı bir arayüz sunduğundan emin olun. Verileri kullanıcı arayüzüne yüklerken, özellikle döndürme gibi yaygın yapılandırma değişikliklerinden sonra gecikmeleri önleyin.

Kullanıcı beklentileri ve sistem davranışı

Kullanıcının yaptığı işleme bağlı olarak etkinlik durumunun temizlenmesini veya korunmasını bekler. Bazı durumlarda sistem, kullanıcının beklediği işlemi otomatik olarak yapar. Diğer durumlarda ise sistem tam tersini yapar.

Kullanıcı tarafından başlatılan kullanıcı arayüzü durumunu kapatma

Kullanıcı, bir etkinliği başlattığında bu etkinliğin geçici kullanıcı arayüzü durumunun, etkinliği tamamen kapatana kadar aynı kalmasını bekliyor. Kullanıcı, aşağıdaki işlemleri yaparak bir etkinliği tamamen kapatabilir:

  • Etkinliği Genel Bakış (Son Kullanılanlar) ekranından kaydırarak kaldırma
  • Uygulamayı Ayarlar ekranından sonlandırma veya kapanmaya zorlama
  • Cihazı yeniden başlatma
  • Bir tür "tamamlama" işlemini gerçekleştirme (Activity.finish() tarafından desteklenir).

Bu tamamen kapatma durumlarında kullanıcının varsayımı, etkinlikten kalıcı olarak ayrıldığıdır. Etkinliği yeniden açarsa etkinliğin temiz bir durumdan başlamasını bekler. Bu kapatma senaryolarındaki temel sistem davranışı, kullanıcı beklentisiyle eşleşir. Etkinlik örneği, içinde depolanan tüm durumlarla ve etkinlikle ilişkili tüm kayıtlı örnek durumu kaydıyla birlikte yok edilir ve bellekten kaldırılır.

Tamamen kapatma ile ilgili bu kuralın bazı istisnaları vardır. Örneğin, bir kullanıcı tarayıcıdan geri düğmesini kullanarak çıkmadan önce baktığı web sayfasına tarayıcının kendisini götürmesini bekleyebilir.

Sistem tarafından başlatılan kullanıcı arayüzü durumunu kapatma

Kullanıcı, döndürme veya çok pencereli moda geçme gibi bir yapılandırma değişikliği sırasında etkinliğin kullanıcı arayüzü durumunun aynı kalmasını bekler. Ancak varsayılan olarak sistem, bu tür bir yapılandırma değişikliği gerçekleştiğinde etkinliği yok eder ve etkinlik örneğinde depolanan tüm kullanıcı arayüzü durumunu siler. Cihaz yapılandırmaları hakkında daha fazla bilgi edinmek için Yapılandırma referans sayfası'na bakın.

Yapılandırma değişiklikleri için varsayılan davranışın geçersiz kılınmasının mümkün olduğunu (ancak önerilmediğini) unutmayın. Daha fazla bilgi için Yapılandırma değişikliğini işleme bölümüne bakın.

Ayrıca, kullanıcılar geçici olarak farklı bir uygulamaya geçip daha sonra uygulamanıza geri döndüklerinde etkinliğinizin kullanıcı arayüzü durumunun aynı kalmasını bekler. Örneğin, kullanıcı arama etkinliğinizde bir arama yapar, ardından ana sayfa düğmesine basar veya bir telefon çağrısını yanıtlar. Arama etkinliğine döndüğünde, arama ağı anahtar kelimesinin ve sonuçların aynen daha önce olduğu gibi orada olmasını bekler.

Bu senaryoda uygulamanız arka plana yerleştirilir ve sistem, uygulama işleminizi bellekte tutmak için elinden geleni yapar. Ancak kullanıcı diğer uygulamalarla etkileşimde bulunurken sistem, uygulama sürecini sonlandırabilir. Bu durumda, etkinlik örneği ve içinde depolanan tüm durumlar yok edilir. Kullanıcı uygulamayı yeniden başlattığında etkinlik beklenmedik bir şekilde temiz durumda oluyor. İşlem sonlandırma hakkında daha fazla bilgi edinmek için İşlemler ve uygulama yaşam döngüsü başlıklı makaleyi inceleyin.

Kullanıcı arayüzü durumunu koruma seçenekleri

Kullanıcının kullanıcı arayüzü durumuyla ilgili beklentileri varsayılan sistem davranışıyla eşleşmediğinde, sistem tarafından başlatılan yok etme işleminin kullanıcı için şeffaf olmasını sağlamak amacıyla kullanıcının kullanıcı arayüzü durumunu kaydedip geri yüklemeniz gerekir.

Kullanıcı arayüzü durumunu korumaya yönelik her seçenek, kullanıcı deneyimini etkileyen aşağıdaki boyutlara göre farklılık gösterir:

ViewModel

Kaydedilmiş örnek durumu

Kalıcı depolama alanı

Depolama konumu

bellek üzerinde

bellek üzerinde

disk veya ağ üzerinde

Yapılandırma değişikliğinden etkilenmez

Evet

Evet

Evet

Sistem tarafından başlatılan işlem sonlandırmadan etkilenmez.

Hayır

Evet

Evet

Kullanıcı etkinliği tamamlandıktan sonra kapatılmaya/finish() işlemine dayanır.

Hayır

Hayır

Evet

Veri sınırlamaları

Karmaşık nesneler kullanılabilir ancak alan, mevcut bellekle sınırlıdır.

yalnızca temel türler ve String gibi basit, küçük nesneler için

yalnızca disk alanı veya ağ kaynağındaki alma maliyeti / zamanı ile sınırlıdır.

Okuma/yazma süresi

hızlı (yalnızca bellek erişimi)

yavaş (serileştirme/seri durumundan çıkarma gerektirir)

yavaş (disk erişimi veya ağ işlemi gerektirir)

Yapılandırma değişikliklerini işlemek için ViewModel'i kullanma

ViewModel, kullanıcı uygulamayı aktif olarak kullanırken kullanıcı arayüzüyle ilgili verileri depolamak ve yönetmek için idealdir. Kullanıcı arayüzü verilerine hızlı erişim sağlar ve döndürme, pencereyi yeniden boyutlandırma ve diğer sık karşılaşılan yapılandırma değişiklikleri sırasında verilerin ağdan veya diskten yeniden getirilmesini önlemenize yardımcı olur. ViewModel'ı nasıl uygulayacağınızı öğrenmek için ViewModel rehberine bakın.

ViewModel, verileri bellekte tutar. Bu nedenle, diskteki veya ağdaki verileri almaktan daha ucuzdur. ViewModel, bir etkinlikle (veya başka bir yaşam döngüsü sahibiyle) ilişkilendirilir. Yapılandırma değişikliği sırasında bellekte kalır ve sistem, yapılandırma değişikliğinden kaynaklanan yeni etkinlik örneğiyle ViewModel'i otomatik olarak ilişkilendirir.

ViewModel'ler, kullanıcı etkinliğinizden veya parçanızdan çıktığında ya da finish() işlevini çağırdığınızda sistem tarafından otomatik olarak yok edilir. Bu da durumun, kullanıcının bu senaryolarda beklediği gibi temizlendiği anlamına gelir.

Kaydedilmiş örnek durumundan farklı olarak, ViewModel'ler sistem tarafından başlatılan bir işlem sonlandırma sırasında yok edilir. Bir ViewModel'de sistem tarafından başlatılan bir işlem sonlandırmasından sonra verileri yeniden yüklemek için SavedStateHandle API'yi kullanın. Alternatif olarak, veriler kullanıcı arayüzüyle ilgiliyse ve ViewModel'de tutulması gerekmiyorsa onSaveInstanceState() kullanın. Veriler uygulama verileriyse diske kalıcı olarak yazmak daha iyi olabilir.

Yapılandırma değişiklikleri sırasında kullanıcı arayüzü durumunuzu depolamak için zaten bir bellek içi çözümünüz varsa ViewModel'i kullanmanız gerekmeyebilir.

Sistemin başlattığı işlem sonlandırmayı işlemek için kaydedilmiş örnek durumunu yedek olarak kullanma

Görünüm sistemindeki onSaveInstanceState() geri çağırma ve ViewModel'lerdeki SavedStateHandle, sistemin bir kullanıcı arayüzü denetleyicisini (ör. etkinlik veya parça) yok edip daha sonra yeniden oluşturması durumunda bu denetleyicinin durumunu yeniden yüklemek için gereken verileri depolar. onSaveInstanceState kullanarak kaydedilmiş örnek durumunu nasıl uygulayacağınızı öğrenmek için Activity Lifecycle Guide'daki Saving and restoring activity state (Etkinlik durumunu kaydetme ve geri yükleme) bölümüne bakın.

Kaydedilmiş örnek durumu paketleri, hem yapılandırma değişiklikleri hem de işlem sonlandırma boyunca kalıcı olur ancak farklı API'ler verileri serileştirdiğinden depolama ve hız açısından sınırlıdır. Serileştirilen nesneler karmaşıksa serileştirme çok fazla bellek tüketebilir. Bu işlem, yapılandırma değişikliği sırasında ana iş parçacığında gerçekleştiğinden uzun süren serileştirme, karelerin düşmesine ve görsel titremeye neden olabilir.

Kaydedilmiş örnek durumunu, bit eşlemler gibi büyük miktarda veri veya uzun serileştirme ya da seri hâlden çıkarma gerektiren karmaşık veri yapılarını depolamak için kullanmayın. Bunun yerine, yalnızca temel türleri ve String gibi basit, küçük nesneleri depolayın. Bu nedenle, diğer kalıcılık mekanizmalarının başarısız olması durumunda kullanıcı arayüzünü önceki durumuna geri yüklemek için gereken verileri yeniden oluşturmak üzere gerekli minimum miktarda veriyi (ör. kimlik) depolamak için kaydedilmiş örnek durumunu kullanın. Çoğu uygulamanın, sistem tarafından başlatılan işlem sonlandırmayı yönetmek için bunu uygulaması gerekir.

Uygulamanızın kullanım alanlarına bağlı olarak, kaydedilmiş örnek durumunu hiç kullanmanız gerekmeyebilir. Örneğin, bir tarayıcı, kullanıcıyı tarayıcıdan çıkmadan önce baktığı web sayfasına geri götürebilir. Etkinliğiniz bu şekilde davranıyorsa kaydedilmiş örnek durumunu kullanmaktan vazgeçebilir ve bunun yerine her şeyi yerel olarak kalıcı hale getirebilirsiniz.

Ayrıca, bir etkinliği bir amaçtan açtığınızda, ekstralar paketi hem yapılandırma değiştiğinde hem de sistem etkinliği geri yüklediğinde etkinliğe teslim edilir.

Bu senaryoların her ikisinde de, yapılandırma değişikliği sırasında veritabanından veri yeniden yükleme döngülerini boşa harcamamak için ViewModel kullanmaya devam etmeniz gerekir.

Korunacak kullanıcı arayüzü verilerinin basit ve küçük olduğu durumlarda, durum verilerinizi korumak için yalnızca kaydedilmiş örnek durumu API'lerini kullanabilirsiniz.

SavedStateRegistry'yi kullanarak kayıtlı duruma bağlanma

Fragment 1.1.0 veya geçişli bağımlılığı Activity 1.0.0'dan itibaren Activity veya Fragment gibi kullanıcı arayüzü denetleyicileri SavedStateRegistryOwner'ı uygular ve bu denetleyiciye bağlı bir SavedStateRegistry sağlar. SavedStateRegistry, bileşenlerin, kullanıcı arayüzü denetleyicinizin kaydedilmiş durumuna bağlanarak bu durumu kullanmasına veya bu duruma katkıda bulunmasına olanak tanır. Örneğin, ViewModel için Kaydedilmiş Durum modülü, SavedStateRegistry kullanarak SavedStateHandle oluşturur ve bunu ViewModel nesnelerinize sağlar. getSavedStateRegistry işlevini çağırarak kullanıcı arayüzü denetleyicinizden SavedStateRegistry değerini alabilirsiniz.

Kaydedilmiş duruma katkıda bulunan bileşenler, saveState adlı tek bir yöntemi tanımlayan SavedStateRegistry.SavedStateProvider arayüzünü uygulamalıdır. saveState() yöntemi, bileşeninizin Bundle döndürmesine olanak tanır. Bu Bundle, bileşenden kaydedilmesi gereken tüm durumları içerir. SavedStateRegistry, bu yöntemi kullanıcı arayüzü denetleyicisinin yaşam döngüsünün kaydetme durumu aşamasında çağırır.

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String QUERY = "query";
    private String query = null;
    ...

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }
}

SavedStateProvider kaydetmek için SavedStateRegistry üzerinde registerSavedStateProvider() numarasını arayarak sağlayıcıyla ilişkilendirilecek bir anahtarın yanı sıra sağlayıcıyı da iletin. Sağlayıcı için daha önce kaydedilen veriler, SavedStateRegistry üzerinde consumeRestoredStateForKey() çağrılarak ve sağlayıcının verileriyle ilişkili anahtar iletilerek kaydedilmiş durumdan alınabilir.

Activity veya Fragment içinde, super.onCreate()'ı aradıktan sonra onCreate()'de SavedStateProvider kaydedebilirsiniz. Alternatif olarak, LifecycleOwner uygulayan bir SavedStateRegistryOwner üzerinde LifecycleObserver ayarlayabilir ve ON_CREATE etkinliği gerçekleştiğinde SavedStateProvider öğesini kaydedebilirsiniz. LifecycleObserver kullanarak daha önce kaydedilen durumun kaydedilmesini ve alınmasını SavedStateRegistryOwner'dan ayırabilirsiniz.

Kotlin

class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val PROVIDER = "search_manager"
        private const val QUERY = "query"
    }

    private val query: String? = null

    init {
        // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE
        registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_CREATE) {
                val registry = registryOwner.savedStateRegistry

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this)

                // Get the previously saved state and restore it
                val state = registry.consumeRestoredStateForKey(PROVIDER)

                // Apply the previously saved state
                query = state?.getString(QUERY)
            }
        }
    }

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }

    ...
}

class SearchFragment : Fragment() {
    private var searchManager = SearchManager(this)
    ...
}

Java

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String PROVIDER = "search_manager";
    private static String QUERY = "query";
    private String query = null;

    public SearchManager(SavedStateRegistryOwner registryOwner) {
        registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
            if (event == Lifecycle.Event.ON_CREATE) {
                SavedStateRegistry registry = registryOwner.getSavedStateRegistry();

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this);

                // Get the previously saved state and restore it
                Bundle state = registry.consumeRestoredStateForKey(PROVIDER);

                // Apply the previously saved state
                if (state != null) {
                    query = state.getString(QUERY);
                }
            }
        });
    }

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }

    ...
}

class SearchFragment extends Fragment {
    private SearchManager searchManager = new SearchManager(this);
    ...
}

Karmaşık veya büyük verilerde işlem sonlandırmayı yönetmek için yerel kalıcılığı kullanma

Veritabanı veya paylaşılan tercihler gibi kalıcı yerel depolama alanları, uygulamanız kullanıcının cihazında yüklü olduğu sürece (kullanıcı uygulamanızın verilerini temizlemediği sürece) kalır. Bu tür yerel depolama alanı, sistem tarafından başlatılan etkinliklere ve uygulama sürecinin sonlandırılmasına rağmen varlığını sürdürse de yerel depolama alanından belleğe okunması gerektiğinden alınması maliyetli olabilir. Bu kalıcı yerel depolama alanı, etkinliği açıp kapattığınızda kaybetmek istemediğiniz tüm verileri depolamak için genellikle uygulama mimarinizin bir parçası olabilir.

ViewModel ve kaydedilmiş örnek durumu, uzun vadeli depolama çözümleri değildir. Bu nedenle, veritabanı gibi yerel depolama alanlarının yerine kullanılamazlar. Bunun yerine, bu mekanizmaları yalnızca geçici kullanıcı arayüzü durumunu geçici olarak depolamak için kullanmalı ve diğer uygulama verileri için kalıcı depolama alanını kullanmalısınız. Uygulama modeli verilerinizi uzun süre (ör. cihaz yeniden başlatıldığında) kalıcı hale getirmek için yerel depolamadan nasıl yararlanacağınız hakkında daha fazla bilgi edinmek için Uygulama Mimarisi Kılavuzu'na göz atın.

Kullanıcı arayüzü durumunu yönetme: Böl ve fethet

Çalışmayı çeşitli kalıcılık mekanizmaları arasında bölerek kullanıcı arayüzü durumunu verimli bir şekilde kaydedip geri yükleyebilirsiniz. Çoğu durumda, bu mekanizmaların her biri, veri karmaşıklığı, erişim hızı ve kullanım ömrü arasındaki dengeye bağlı olarak etkinlikte kullanılan farklı bir veri türünü saklamalıdır:

  • Yerel kalıcılık: Etkinliği açıp kapattığınızda kaybetmek istemediğiniz tüm uygulama verilerini depolar.
    • Örnek: Ses dosyaları ve meta veriler içerebilen bir şarkı nesneleri koleksiyonu.
  • ViewModel: İlişkili kullanıcı arayüzünü görüntülemek için gereken tüm verileri, ekran kullanıcı arayüzü durumunu bellekte depolar.
    • Örnek: En son aramanın şarkı nesneleri ve en son arama sorgusu.
  • Kaydedilmiş örnek durumu: Sistem durup kullanıcı arayüzünü yeniden oluşturursa kullanıcı arayüzü durumunu yeniden yüklemek için gereken küçük miktardaki veriyi depolar. Karmaşık nesneleri burada depolamak yerine, karmaşık nesneleri yerel depolamada kalıcı hale getirin ve bu nesneler için benzersiz bir kimliği kaydedilmiş örnek durumu API'lerinde saklayın.
    • Örnek: En son arama sorgusunu saklama.

Örneğin, şarkı kitaplığınızda arama yapmanıza olanak tanıyan bir etkinliği ele alalım. Farklı etkinliklerin nasıl ele alınması gerektiği aşağıda açıklanmıştır:

Kullanıcı bir şarkı eklediğinde ViewModel, bu verilerin yerel olarak kalıcı hale getirilmesini hemen devreder. Yeni eklenen bu şarkının kullanıcı arayüzünde gösterilmesi gerekiyorsa ViewModel nesnesindeki verileri de şarkının eklenmesini yansıtacak şekilde güncellemeniz gerekir. Tüm veritabanı ekleme işlemlerini ana iş parçacığı dışında yapmayı unutmayın.

Kullanıcı bir şarkı aradığında, veritabanından yüklediğiniz karmaşık şarkı verileri ekran kullanıcı arayüzü durumunun bir parçası olarak ViewModel nesnesinde hemen depolanmalıdır.

Etkinlik arka plana alındığında ve sistem, kaydedilmiş örnek durumu API'lerini çağırdığında, işlemin yeniden oluşturulması ihtimaline karşı arama sorgusu kaydedilmiş örnek durumunda saklanmalıdır. Bilgiler, bu alanda kalıcı hale getirilen uygulama verilerini yüklemek için gerekli olduğundan arama sorgusunu ViewModel'de saklayın SavedStateHandle. Verileri yüklemek ve kullanıcı arayüzünü mevcut durumuna geri getirmek için gereken tüm bilgiler burada yer alır.

Karmaşık durumları geri yükleme: parçaları yeniden birleştirme

Kullanıcının etkinliğe dönme zamanı geldiğinde etkinliği yeniden oluşturmak için iki olası senaryo vardır:

  • Etkinlik, sistem tarafından durdurulduktan sonra yeniden oluşturulur. Sistem, sorguyu kayıtlı örnek durumu paketinde kaydeder ve ViewModel kullanılmıyorsa kullanıcı arayüzü sorguyu SavedStateHandle'ye iletmelidir. ViewModel, önbelleğe alınmış arama sonucu olmadığını görür ve verilen arama sorgusunu kullanarak arama sonuçlarını yükleme işlemini devreder.
  • Etkinlik, yapılandırma değişikliğinden sonra yeniden oluşturulur. ViewModel örneği yok edilmediğinden ViewModel, bellekteki tüm bilgileri önbelleğe alır ve veritabanına yeniden sorgu göndermesi gerekmez.

Ek kaynaklar

Kullanıcı arayüzü durumlarını kaydetme hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın.

Bloglar

Codelab uygulamaları