ViewModel için Kayıtlı Durum modülü (Görünümler)   Android Jetpack'in bir parçasıdır.

Kavramlar ve Jetpack Compose uygulaması

Kullanıcı Arayüzü Durumlarını Kaydetme bölümünde belirtildiği gibi, ViewModel nesneleri yapılandırma değişikliklerini işleyebilir. Bu nedenle, döndürme veya diğer durumlarda durumla ilgili endişelenmenize gerek yoktur. Ancak sistem tarafından başlatılan süreç sonlandırmalarını işlemeniz gerekiyorsa yedek olarak SavedStateHandle API'sini kullanabilirsiniz.

Kullanıcı arayüzü durumu genellikle ViewModel nesnelerinde depolanır veya bunlara referans verilir ve etkinliklerde depolanmaz. Bu nedenle, onSaveInstanceState() kullanmak için kaydedilmiş durum modülünün sizin için işleyebileceği bazı standart kodlar gerekir.

Bu modül kullanılırken ViewModel nesneleri, oluşturucusu aracılığıyla SavedStateHandle nesnesi alır. Bu nesne, kaydedilmiş duruma nesne yazmanıza ve nesne almanıza olanak tanıyan bir anahtar/değer çifti haritasıdır. Bu değerler, işlem sistem tarafından sonlandırıldıktan sonra da kalıcı olur ve aynı nesne üzerinden kullanılmaya devam eder.

Kaydedilen durum, görev yığınına bağlıdır. Görev yığınınız kaybolursa kaydedilen durumunuz da kaybolur. Bu durum, bir uygulama zorla durdurulduğunda, uygulamayı son kullanılanlar menüsünden kaldırdığınızda veya cihazı yeniden başlattığınızda ortaya çıkabilir. Bu gibi durumlarda görev yığını kaybolur ve kaydedilen durumdaki bilgileri geri yükleyemezsiniz. Kullanıcı tarafından başlatılan kullanıcı arayüzü durumunu kapatma senaryolarında, kaydedilen durum geri yüklenmez. Sistem tarafından başlatılan senaryolarda bu durum geçerlidir.

Kurulum

Fragment 1.2.0 veya geçişli bağımlılığı Activity 1.1.0'dan itibaren, SavedStateHandle öğesini ViewModel öğenizin oluşturucu bağımsız değişkeni olarak kabul edebilirsiniz.

Kotlin

class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }

Java

public class SavedStateViewModel extends ViewModel {
    private SavedStateHandle state;

    public SavedStateViewModel(SavedStateHandle savedStateHandle) {
        state = savedStateHandle;
    }

    ...
}

Ardından, ek bir yapılandırma yapmadan ViewModel örneğini alabilirsiniz. Varsayılan ViewModel fabrikası, ViewModel için uygun SavedStateHandle değerini sağlar.

Kotlin

class MainFragment : Fragment() {
    val vm: SavedStateViewModel by viewModels()

    ...
}

Java

class MainFragment extends Fragment {
    private SavedStateViewModel vm;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        vm = new ViewModelProvider(this).get(SavedStateViewModel.class);

        ...

    }

    ...
}

Özel bir ViewModelProvider.Factory örneği sağlarken SavedStateHandle kullanımını AbstractSavedStateViewModelFactory'ı genişleterek etkinleştirebilirsiniz.

SavedStateHandle ile çalışma

SavedStateHandle sınıfı, set() ve get() yöntemleriyle kaydedilmiş durumdan veri yazmanıza ve veri almanıza olanak tanıyan bir anahtar/değer eşlemesidir.

SavedStateHandle kullanıldığında sorgu değeri, işlem sonlandığında korunur. Böylece, etkinlik veya parçanın bu değeri manuel olarak kaydetmesi, geri yüklemesi ve SavedStateHandle'ya geri iletmesi gerekmeden, kullanıcı yeniden oluşturmadan önce ve sonra aynı filtrelenmiş veri grubunu görür.ViewModel

SavedStateHandle ayrıca bir anahtar/değer çifti haritasıyla etkileşimde bulunurken bekleyebileceğiniz başka yöntemler de içerir:

  • contains(String key): Belirli bir anahtar için değer olup olmadığını kontrol eder.
  • remove(String key): Belirli bir anahtarın değerini kaldırır.
  • keys(): SavedStateHandle içinde bulunan tüm anahtarları döndürür.

Ayrıca, gözlemlenebilir bir veri tutucu kullanarak SavedStateHandle değerlerini de alabilirsiniz. Desteklenen türlerin listesi:

LiveData

getLiveData() kullanarak LiveData observable'a sarılmış SavedStateHandle değerlerini alın. Anahtarın değeri güncellendiğinde LiveData yeni değeri alır. Değer genellikle, bir veri listesini filtrelemek için sorgu girme gibi kullanıcı etkileşimleri nedeniyle ayarlanır. Bu güncellenen değer daha sonra dönüşüm LiveData için kullanılabilir.

Kotlin

class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    val filteredData: LiveData<List<String>> =
        savedStateHandle.getLiveData<String>("query").switchMap { query ->
        repository.getFilteredData(query)
    }

    fun setQuery(query: String) {
        savedStateHandle["query"] = query
    }
}

Java

public class SavedStateViewModel extends ViewModel {
    private SavedStateHandle savedStateHandle;
    public LiveData<List<String>> filteredData;
    public SavedStateViewModel(SavedStateHandle savedStateHandle) {
        this.savedStateHandle = savedStateHandle;
        LiveData<String> queryLiveData = savedStateHandle.getLiveData("query");
        filteredData = Transformations.switchMap(queryLiveData, query -> {
            return repository.getFilteredData(query);
        });
    }

    public void setQuery(String query) {
        savedStateHandle.set("query", query);
    }
}

Desteklenen türler

SavedStateHandle içinde tutulan veriler, etkinliğin veya parçanın geri kalanı olan savedInstanceState ile birlikte Bundle olarak kaydedilir ve geri yüklenir.

Paketlenebilir olmayan sınıfları kaydetme

Bir sınıf Parcelable veya Serializable uygulamıyorsa ve bu arayüzlerden birini uygulayacak şekilde değiştirilemiyorsa bu sınıfın bir örneğini doğrudan SavedStateHandle içine kaydetmek mümkün değildir.

Lifecycle 2.3.0-alpha03'ten itibaren SavedStateHandle, setSavedStateProvider() yöntemini kullanarak nesnenizi Bundle olarak kaydetme ve geri yükleme mantığınızı sağlayarak herhangi bir nesneyi kaydetmenize olanak tanır. SavedStateRegistry.SavedStateProvider, kaydetmek istediğiniz durumu içeren bir Bundle döndüren tek bir saveState() yöntemini tanımlayan bir arayüzdür. SavedStateHandle durumu kaydetmeye hazır olduğunda saveState() işlevini çağırarak SavedStateProvider içindeki Bundle değerini alır ve ilişkili anahtar için Bundle değerini kaydeder.

Kamera uygulamasından ACTION_IMAGE_CAPTURE intent'i aracılığıyla resim isteyen bir uygulamayı ele alalım. Bu uygulama, kameranın resmi depolayacağı yer için geçici bir dosya gönderir. TempFileViewModel, bu geçici dosyayı oluşturma mantığını kapsar.

Kotlin

class TempFileViewModel : ViewModel() {
    private var tempFile: File? = null

    fun createOrGetTempFile(): File {
        return tempFile ?: File.createTempFile("temp", null).also {
            tempFile = it
        }
    }
}

Java

class TempFileViewModel extends ViewModel {
    private File tempFile = null;

    public TempFileViewModel() {
    }

    @NonNull
    public File createOrGetTempFile() {
        if (tempFile == null) {
            tempFile = File.createTempFile("temp", null);
        }
        return tempFile;
    }
}

Etkinliğin işlemi sonlandırılıp daha sonra geri yüklenirse geçici dosyanın kaybolmaması için TempFileViewModel, verilerini kalıcı hale getirmek üzere SavedStateHandle kullanabilir. TempFileViewModel uygulamasının verilerini kaydetmesine izin vermek için SavedStateProvider uygulayın ve ViewModel uygulamasının SavedStateHandle üzerinde sağlayıcı olarak ayarlayın:

Kotlin

private fun File.saveTempFile() = bundleOf("path", absolutePath)

class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    private var tempFile: File? = null
    init {
        savedStateHandle.setSavedStateProvider("temp_file") { // saveState()
            if (tempFile != null) {
                tempFile.saveTempFile()
            } else {
                Bundle()
            }
        }
    }

    fun createOrGetTempFile(): File {
        return tempFile ?: File.createTempFile("temp", null).also {
            tempFile = it
        }
    }
}

Java

class TempFileViewModel extends ViewModel {
    private File tempFile = null;

    public TempFileViewModel(SavedStateHandle savedStateHandle) {
        savedStateHandle.setSavedStateProvider("temp_file",
            new TempFileSavedStateProvider());
    }
    @NonNull
    public File createOrGetTempFile() {
        if (tempFile == null) {
            tempFile = File.createTempFile("temp", null);
        }
        return tempFile;
    }

    private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider {
        @NonNull
        @Override
        public Bundle saveState() {
            Bundle bundle = new Bundle();
            if (tempFile != null) {
                bundle.putString("path", tempFile.getAbsolutePath());
            }
            return bundle;
        }
    }
}

Kullanıcı geri döndüğünde File verilerini geri yüklemek için temp_file Bundle değerini SavedStateHandle konumundan alın. Bu, mutlak yolu içeren saveTempFile() tarafından sağlanan Bundle ile aynıdır. Daha sonra mutlak yol, yeni bir File oluşturmak için kullanılabilir.

Kotlin

private fun File.saveTempFile() = bundleOf("path", absolutePath)

private fun Bundle.restoreTempFile() = if (containsKey("path")) {
    File(getString("path"))
} else {
    null
}

class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    private var tempFile: File? = null
    init {
        val tempFileBundle = savedStateHandle.get<Bundle>("temp_file")
        if (tempFileBundle != null) {
            tempFile = tempFileBundle.restoreTempFile()
        }
        savedStateHandle.setSavedStateProvider("temp_file") { // saveState()
            if (tempFile != null) {
                tempFile.saveTempFile()
            } else {
                Bundle()
            }
        }
    }

    fun createOrGetTempFile(): File {
      return tempFile ?: File.createTempFile("temp", null).also {
          tempFile = it
      }
    }
}

Java

class TempFileViewModel extends ViewModel {
    private File tempFile = null;

    public TempFileViewModel(SavedStateHandle savedStateHandle) {
        Bundle tempFileBundle = savedStateHandle.get("temp_file");
        if (tempFileBundle != null) {
            tempFile = TempFileSavedStateProvider.restoreTempFile(tempFileBundle);
        }
        savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider());
    }

    @NonNull
    public File createOrGetTempFile() {
        if (tempFile == null) {
            tempFile = File.createTempFile("temp", null);
        }
        return tempFile;
    }

    private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider {
        @NonNull
        @Override
        public Bundle saveState() {
            Bundle bundle = new Bundle();
            if (tempFile != null) {
                bundle.putString("path", tempFile.getAbsolutePath());
            }
            return bundle;
        }

        @Nullable
        private static File restoreTempFile(Bundle bundle) {
            if (bundle.containsKey("path") {
                return File(bundle.getString("path"));
            }
            return null;
        }
    }
}