Kullanıcı arayüzü durumlarını kaydet

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

Bir etkinliğin kullanıcı arayüzü durumunun sistem tarafından etkinlikleri veya uygulamaları kaldırmasından hemen sonra kaydedilmesi ve geri yüklenmesi iyi bir kullanıcı deneyimi için esastır. 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 boşluğu kapatmak için aşağıdaki yöntemlerin bir birleşimini kullanın:

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 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. Özellikle döndürme gibi yaygın yapılandırma değişikliklerinden sonra, verileri kullanıcı arayüzüne yüklerken gecikmeleri önleyin.

Kullanıcı beklentileri ve sistem davranışı

Yaptığı işleme bağlı olarak, kullanıcılar bu etkinlik durumunun temizlenmesini veya korunmasını bekler. Bazı durumlarda sistem, kullanıcının beklediği şeyi otomatik olarak yapar. Diğer durumlarda ise sistem, kullanıcının beklediğinin tersini yapar.

Kullanıcı tarafından başlatılan kullanıcı arayüzü durumu reddi

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

  • Etkinliği Genel Bakış (Son Kullanılanlar) ekranından kaydırarak kaldırın.
  • Ayarlar ekranından uygulamayı öldürme veya zorla çıkma.
  • Cihaz yeniden başlatılıyor.
  • Bir tür "tamamlama" işlemi (Activity.finish() ile desteklenir) tamamlamak.

Bu eksiksiz kapatma davalarında kullanıcının, etkinlikten kalıcı olarak uzaklaştığı ve etkinliği yeniden açarsa etkinliğin temiz bir durumda başlamasını beklediği varsayımında bulunur. Bu kapatma senaryolarında temel sistem davranışı, kullanıcının beklentisini karşılar. Etkinlik örneği, depolanan tüm durumlar ve etkinlikle ilişkili kaydedilmiş örnek durum kayıtlarıyla birlikte imha edilir ve bellekten kaldırılır.

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

Sistem tarafından başlatılan kullanıcı arayüzü durumu için kapatma

Kullanıcı, bir etkinliğin kullanıcı arayüzü durumunun, döndürme veya çoklu pencere moduna geçme gibi yapılandırma değişiklikleri boyunca aynı kalmasını bekler. Bununla birlikte, varsayılan olarak bu tür bir yapılandırma değişikliği gerçekleştiğinde sistem etkinliği yok eder ve etkinlik örneğinde depolanan tüm kullanıcı arayüzü durumları silinir. Cihaz yapılandırmaları hakkında daha fazla bilgi 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ınabileceğini (önerilmez) unutmayın. Daha ayrıntılı bilgi için Yapılandırma değişikliğini kendiniz ele alma bölümüne bakın.

Kullanıcılar geçici olarak farklı bir uygulamaya geçip daha sonra uygulamanıza geri dönerse etkinliğinizin kullanıcı arayüzü durumunun aynı kalmasını bekler. Örneğin, kullanıcı, arama etkinliğinizde bir arama yapar ve ardından ana sayfa düğmesine basar veya bir telefon çağrısına cevap verir. Arama etkinliğine döndüğünde, arama anahtar kelimesini ve sonuçlarını önceden olduğu gibi hâlâ orada bulmayı bekler.

Bu senaryoda uygulamanız arka plana yerleştirilir. Sistem, uygulama sürecinizi bellekte tutmak için elinden geleni yapar. Ancak kullanıcı diğer uygulamalarla etkileşimde değilken sistem uygulama işlemini bozabilir. Böyle bir durumda, etkinlik örneği ve içinde depolanan tüm durumlar kaldırılır. Kullanıcı uygulamayı yeniden başlattığında etkinlik beklenmedik bir şekilde temiz bir duruma geçer. İşlem ölümü hakkında daha fazla bilgi edinmek için İşlemler ve Uygulama Yaşam Döngüsü bölümüne bakın.

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

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

Kullanıcı arayüzü durumunu koruma seçeneklerinin her biri, kullanıcı deneyimini etkileyen aşağıdaki boyutlara göre değişiklik gösterir:

ViewModel Kaydedilen örnek durumu Kalıcı depolama
Depolama alanı konumu bellek üzerinde bellek üzerinde disk veya ağda
Yapılandırma değişikliğinden sonra kurtulur Evet Evet Evet
Sistem tarafından başlatılan işlemin ölümünden sonra kurtulur Hayır Evet Evet
Kullanıcının etkinliği tamamlamasından sonra kurtarma/onFinish() Hayır Hayır Evet
Veri sınırlamaları karmaşık nesneler kullanılabilir ancak alan, kullanılabilir bellekle sınırlıdır String gibi basit türler ve basit, küçük nesneler için yalnızca disk alanı veya ağ kaynağından alma maliyeti / zamanı ile sınırlıdır
Okuma/yazma süresi hızlı (yalnızca bellek erişimi) yavaş (serileştirme/serileştirme 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ü ile ilgili verileri depolamak ve yönetmek için idealdir. Bu arayüz, kullanıcı arayüzü verilerine hızlı erişim sağlar. Ayrıca rotasyon, pencere yeniden boyutlandırma ve sık yapılan diğer yapılandırma değişiklikleri sırasında ağ veya diskten verilerin yeniden getirilmesini önlemenize yardımcı olur. Bir ViewModel'i nasıl uygulayacağınızı öğrenmek için ViewModel kılavuzuna bakın.

ViewModel, verileri bellekte tutar. Bu, verilerin disk veya ağdan alınmasından daha ucuz olduğu anlamına gelir. 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, ViewModel'i yapılandırma değişikliğinden kaynaklanan yeni etkinlik örneğiyle otomatik olarak ilişkilendirir.

ViewModel'ler, kullanıcınız etkinliğinize veya parçanıza çekildiğinde sistem tarafından otomatik olarak yok edilir. finish() yöntemini çağırırsanız durum, kullanıcının bu senaryolarda beklediği şekilde temizlenir.

Kaydedilen örnek durumunun aksine ViewModel'ler, sistem tarafından başlatılan bir işlem ölümü sırasında yok edilir. ViewModel'de sistem tarafından başlatılan işlem ölümünden 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 View sisteminde onSaveInstanceState() veya Jetpack Composer'da rememberSaveable kullanın. Veriler uygulama verileri ise bunların diskte tutulması daha iyi olabilir.

Yapılandırma değişiklikleri genelinde kullanıcı arayüzü durumunuzu depolamak için halihazırda kullandığınız bir bellek içi çözümünüz varsa ViewModel'i kullanmanız gerekmeyebilir.

Sistem tarafından başlatılan işlem ölümünü işlemek için kayıtlı örnek durumunu yedek olarak kullan

View sistemindeki onSaveInstanceState() geri çağırması, Jetpack Compose'daki rememberSaveable ve ViewModels'teki SavedStateHandle, sistem söz konusu denetleyiciyi kaldırıp daha sonra yeniden oluşturursa etkinlik veya parça gibi bir kullanıcı arayüzü denetleyicisinin durumunu yeniden yüklemek için gereken verileri depolar. Kaydedilen örnek durumunun onSaveInstanceState kullanarak nasıl uygulanacağını öğrenmek için Etkinlik Yaşam Döngüsü kılavuzunda Etkinlik durumunu kaydetme ve geri yükleme bölümüne bakın.

Kaydedilen örnek durum paketleri, hem yapılandırma değişiklikleri hem de işlem ölümü durumunda kalır ancak farklı API'ler verileri serileştirdiği için depolama ve hız ile sınırlıdır. Serileştirmedeki 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üre çalışan serileştirme, çerçevelerin düşmesine ve görsel takılmalara neden olabilir.

Kayıtlı örnek durumunu, bit eşlemler gibi büyük miktarda veriyi ve uzun süreli serileştirme ya da serileştirme gerektiren karmaşık veri yapılarını depolamak için kullanmayın. Bunun yerine, yalnızca basit türleri ve String gibi basit, küçük nesneleri depolayın. Bu nedenle, diğer kalıcılık mekanizmalarının arızalanması durumunda kullanıcı arayüzünü önceki durumuna geri yüklemek için gereken verileri yeniden oluşturmak amacıyla gerekli minimum miktarda veriyi (ör. kimlik) depolamak için kayıtlı örnek durumunu kullanın. Çoğu uygulama, sistem tarafından başlatılan işlem ölümünü işlemek için bunu uygulamalıdır.

Uygulamanızın kullanım alanlarına bağlı olarak, kayıtlı örnek durumunu hiç kullanmanız gerekmeyebilir. Örneğin, bir tarayıcı kullanıcıyı tarayıcıdan çıkmadan önce tam olarak baktığı web sayfasına götürebilir. Etkinliğiniz bu şekilde davranıyorsa kayıtlı örnek durumunu kullanmayı bırakıp her şeyi yerel olarak devam ettirebilirsiniz.

Buna ek olarak, bir amaca ait bir etkinliği 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 iletilir. Etkinlik başlatıldığında arama sorgusu gibi bir kullanıcı arayüzü durum verisi parçası ekstra bir amaç olarak aktarıldıysa kayıtlı örnek durumu paketi yerine ekstralar paketini kullanabilirsiniz. Amaç ekstraları hakkında daha fazla bilgi edinmek için Amaç ve Amaç Filtreleri bölümüne bakın.

Her iki senaryoda da yapılandırma değişikliği sırasında veritabanından verileri yeniden yükleme döngülerinin boşa gitmesini önlemek için ViewModel kullanmanız gerekir.

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

SavedStateRegistry'yi kullanarak kayıtlı duruma bağlanın

Fragment 1.1.0 veya geçişli bağımlılığı olan Activity 1.0.0'dan başlayarak, Activity veya Fragment gibi kullanıcı arayüzü denetleyicileri SavedStateRegistryOwner uygular ve bu denetleyiciye bağlı bir SavedStateRegistry sağlar. SavedStateRegistry, bileşenlerin tüketmek veya katkıda bulunmak için kullanıcı arayüzü denetleyicinizin kayıtlı durumuna bağlanmasına olanak tanır. Örneğin, ViewModel için Kaydedilen Durum modülü bir SavedStateHandle oluşturmak ve bunu ViewModel nesnelerinize sağlamak için SavedStateRegistry kullanır. getSavedStateRegistry() yöntemini çağırarak kullanıcı arayüzü denetleyicinizden SavedStateRegistry kodunu alabilirsiniz.

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

Kotlin

class SearchManager : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val QUERY = "query"
    }

    private val query: String? = null

    ...

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

Java

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;
    }
}

Bir SavedStateProvider kaydı yapmak için SavedStateRegistry üzerinde registerSavedStateProvider() çağrısı ile hem sağlayıcının hem de sağlayıcının verileriyle ilişkilendirmek için bir anahtar iletin. Sağlayıcı için daha önce kaydedilmiş veriler, SavedStateRegistry üzerinde consumeRestoredStateForKey() çağrısı yapılarak ve sağlayıcının verileriyle ilişkili anahtarın aktarılmasıyla kayıtlı durumdan alınabilir.

Bir Activity veya Fragment içinde super.onCreate() çağırdıktan sonra onCreate() uygulamasında SavedStateProvider kaydettirebilirsiniz. Alternatif olarak, LifecycleOwner uygulayan bir SavedStateRegistryOwner üzerinde LifecycleObserver ayarlayabilir ve ON_CREATE etkinliği gerçekleştiğinde SavedStateProvider kaydedebilirsiniz. LifecycleObserver kullanarak, önceden kaydedilmiş durumun kaydedilmesini ve geri alınmasını SavedStateRegistryOwner öğesinden 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 sürecin sonlandırılmasıyla ilgili işlemleri yapmak için yerel kalıcılığı kullanın.

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ımda kalır (kullanıcı, uygulamanızın verilerini temizlemediği sürece). Bu tür yerel depolama, sistem tarafından başlatılan etkinlik ve uygulama işlemi ölümlerinde hayatta kalmaya devam etse de yerel depolama alanından belleğe okunması gerekeceğinden verilerin alınması pahalı olabilir. Bu kalıcı yerel depolama, genellikle etkinliği açıp kapattığınızda kaybetmek istemediğiniz tüm verileri depolamak için uygulama mimarinizin bir parçası olabilir.

Ne ViewModel ne de kayıtlı örnek durumu uzun vadeli depolama çözümleri değildir ve bu nedenle, veritabanı gibi yerel depolamaların yerine geçmez. Bunun yerine, bu mekanizmaları yalnızca geçici kullanıcı arayüzü durumunu geçici olarak depolamak ve diğer uygulama verileri için kalıcı depolama alanını kullanmanız gerekir. Uygulama modeli verilerinizi uzun vadede (ör. cihazı yeniden başlattığınızda) saklamak için yerel depolama alanından nasıl yararlanacağınız hakkında daha ayrıntılı bilgi için Uygulama Mimarisi Rehberi'ne bakın.

Kullanıcı arayüzü durumunu yönetme: bölme ve fethetme

İşi, çeşitli kalıcılık mekanizması türlerine bölerek kullanıcı arayüzü durumunu etkili bir şekilde kaydedip geri yükleyebilirsiniz. Çoğu durumda bu mekanizmaların her birinin; veri karmaşıklığı, erişim hızı ve kullanım ömrü avantajlarına göre etkinlikte kullanılan farklı türde verileri depolaması gerekir:

  • 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 verileri içerebilen şarkı nesnelerinden oluşan bir koleksiyon.
  • ViewModel: İlişkili kullanıcı arayüzünü, ekran kullanıcı arayüzü durumunu görüntülemek için gereken tüm verileri belleğe depolar.
    • Örnek: En son aramanın ve en son arama sorgusunun şarkı nesneleri.
  • Kaydedilen örnek durumu: Sistem durup kullanıcı arayüzünü yeniden oluşturursa kullanıcı arayüzü durumunu yeniden yüklemek için gereken az miktarda veriyi depolar. Karmaşık nesneleri burada depolamak yerine karmaşık nesneleri yerel depolama alanında tutun ve bu nesneler için kaydedilen örnek durumu API'lerinde benzersiz bir kimlik depolayın.
    • Örnek: En son arama sorgusunu depolama.

Örneğin, şarkı kitaplığınızda arama yapmanızı sağlayan bir etkinlik düşünün. Farklı etkinliklerin nasıl ele alınacağı aşağıda açıklanmıştır:

Kullanıcı bir şarkı eklediğinde ViewModel, bu verilerin yerel olarak kalması için yetkiyi hemen verir. Yeni eklenen bu şarkının kullanıcı arayüzünde gösterilmesi gerekiyorsa şarkı ekleneni yansıtmak için ViewModel nesnesindeki verileri de güncellemeniz gerekir. Tüm veritabanı ekleme işlemlerini ana iş parçacığından yapmayı unutmayın.

Kullanıcı bir şarkı aradığında, veritabanından yüklediğiniz karmaşık şarkı verileri ne olursa olsun ekran kullanıcı arayüzü durumu kapsamında hemen ViewModel nesnesinde depolanmalıdır.

Etkinlik arka plana gittiğinde ve sistem, kayıtlı örnek durumu API'lerini çağırdığında, işlemin yeniden oluşturulması ihtimaline karşı arama sorgusu kayıtlı örnek durumunda depolanmalıdır. Buradaki uygulama verilerini yüklemek için gerekli olan bilgiler gerektiğinden, arama sorgusunu ViewModel SavedStateHandle içinde depolayın. Verileri yüklemek ve kullanıcı arayüzünü mevcut durumuna geri getirmek için ihtiyacınız olan tüm bilgiler bunlardı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ı bir örnek durum paketine kaydeder ve SavedStateHandle kullanılmazsa kullanıcı arayüzü, sorguyu ViewModel öğesine iletmelidir. ViewModel, önbelleğe alınan arama sonucunun olmadığını görür ve belirtilen arama sorgusunu kullanarak arama sonuçlarını yükleme yetkisi verir.
  • Etkinlik, bir yapılandırma değişikliğinden sonra oluşturulur. ViewModel örneği yok edilmediği için ViewModel tüm bilgileri bellekte önbelleğe alır ve veritabanını yeniden sorgulamasına gerek yoktur.

Ek kaynaklar

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

Bloglar