Modul Status Tersimpan untuk ViewModel   Bagian dari Android Jetpack.

Seperti yang telah disebutkan dalam Menyimpan Status UI, objek ViewModel dapat menangani perubahan konfigurasi, sehingga Anda tidak perlu khawatir tentang status dalam rotasi atau kasus lainnya. Namun, jika Anda perlu menangani penghentian proses yang diinisiasi sistem, sebaiknya gunakan onSaveInstanceState() sebagai cadangan.

Status UI biasanya disimpan atau dirujuk dalam objek ViewModel dan bukan aktivitas, sehingga penggunaan onSaveInstanceState() memerlukan beberapa boilerplate yang dapat ditangani oleh modul status tersimpan untuk Anda.

Saat menggunakan modul ini, objek ViewModel akan menerima objek SavedStateHandle melalui konstruktornya. Objek ini adalah peta nilai kunci (key value) yang memungkinkan Anda menulis dan mengambil objek ke dan dari status tersimpan. Nilai ini dipertahankan setelah proses dihapus oleh sistem dan tetap tersedia melalui objek yang sama.

Penyiapan

Mulai dari Fragment 1.2.0 atau dependensi transitifnya Activity 1.1.0, Anda dapat menerima SavedStateHandle sebagai argumen konstruktor untuk ViewModel.

Kotlin

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

Java

public class SavedStateViewModel extends ViewModel {
    private SavedStateHandle state;

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

    ...
}

Selanjutnya, Anda dapat mengambil instance ViewModel tanpa konfigurasi tambahan apa pun. Factory ViewModel default menyediakan SavedStateHandle yang sesuai untuk ViewModel Anda.

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

        ...

    }

    ...
}

Saat memberikan instance ViewModelProvider.Factory kustom, Anda dapat mengaktifkan penggunaan SavedStateHandle dengan memperluas AbstractSavedStateViewModelFactory.

Menangani SavedStateHandle

Class SavedStateHandle adalah peta nilai kunci yang memungkinkan Anda menulis dan mengambil data ke dan dari status tersimpan melalui metode set() dan get(). Selain itu, Anda dapat mengambil nilai dari SavedStateHandle yang digabungkan dalam observable LiveData menggunakan getLiveData(). Saat nilai kunci diperbarui, LiveData akan menerima nilai baru. Biasanya, nilai ditetapkan karena interaksi pengguna, seperti memasukkan kueri untuk memfilter daftar data. Nilai yang diperbarui ini selanjutnya dapat digunakan untuk mentransformasi LiveData.

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 UserViewModelJava(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);
    }
}

Dengan menggunakan SavedStateHandle, nilai kueri akan dipertahankan di seluruh penghentian proses, sehingga memastikan pengguna melihat kumpulan data yang difilter yang sama sebelum dan setelah pembuatan ulang tanpa aktivitas atau fragmen perlu secara manual menyimpan, memulihkan, dan meneruskan nilai tersebut kembali ke ViewModel.

SavedStateHandle juga memiliki metode lain yang dapat Anda gunakan saat berinteraksi dengan peta nilai kunci:

Jenis yang didukung

Data yang dipertahankan dalam SavedStateHandle disimpan dan dipulihkan sebagai Bundle, beserta savedInstanceState lainnya untuk aktivitas atau fragmen tersebut.

Jenis yang didukung secara langsung

Secara default, Anda dapat memanggil set() dan get() di SavedStateHandle untuk jenis data yang sama dengan Bundle, seperti yang ditunjukkan di bawah ini:

Dukungan Jenis/Class Dukungan array
double double[]
int int[]
long long[]
String String[]
byte byte[]
char char[]
CharSequence CharSequence[]
float float[]
Parcelable Parcelable[]
Serializable Serializable[]
short short[]
SparseArray
Binder
Bundle
ArrayList
Size (only in API 21+)
SizeF (only in API 21+)

Jika class tidak memperluas salah satu yang ada dalam daftar di atas, pertimbangkan untuk membagi-bagi class dengan menambahkan anotasi Kotlin @Parcelize atau mengimplementasikan Parcelable secara langsung.

Menyimpan class yang tidak dapat dibagi-bagi

Jika class tidak mengimplementasi Parcelable atau Serializable dan tidak dapat dimodifikasi untuk mengimplementasikan salah satu antarmuka tersebut, maka Anda tidak dapat menyimpan instance class tersebut secara langsung ke dalam SavedStateHandle.

Mulai dari Lifecycle 2.3.0-alpha03, SavedStateHandle memungkinkan Anda menyimpan objek apa pun dengan menyediakan logika sendiri untuk menyimpan dan memulihkan objek sebagai Bundle menggunakan metode setSavedStateProvider(). SavedStateRegistry.SavedStateProvider adalah antarmuka yang menentukan satu metode saveState() yang menampilkan Bundle berisi status yang ingin Anda simpan. Saat SavedStateHandle siap menyimpan statusnya, saveState() akan dipanggil untuk mengambil Bundle dari SavedStateProvider dan menyimpan Bundle untuk kunci terkait.

Mari kita gunakan contoh sebuah aplikasi yang meminta gambar dari aplikasi kamera melalui intent ACTION_IMAGE_CAPTURE, yang meneruskan file sementara tempat kamera akan menyimpan gambar. TempFileViewModel merangkum logika untuk membuat file sementara tersebut.

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

Untuk memastikan file sementara tidak hilang jika proses aktivitas dihentikan dan kemudian dipulihkan, TempFileViewModel dapat menggunakan SavedStateHandle untuk mempertahankan datanya. Untuk mengizinkan TempFileViewModel menyimpan datanya, implementasikan SavedStateProvider dan tetapkan sebagai penyedia pada SavedStateHandle dari ViewModel:

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

Untuk memulihkan data File saat pengguna kembali, ambil temp_file Bundle dari SavedStateHandle. Ini adalah Bundle yang sama yang disediakan oleh saveTempFile() yang berisi jalur absolut. Jalur absolut tersebut kemudian dapat digunakan untuk membuat instance File baru.

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

Referensi lainnya

Guna mengetahui informasi modul Status Tersimpan untuk ViewModel lebih lanjut, baca referensi berikut.

Codelab