Menangani Perubahan Waktu Proses

Sebagian konfigurasi perangkat bisa berubah selama waktu proses (seperti orientasi layar, ketersediaan keyboard, dan bahasa). Saat perubahan demikian terjadi, Android akan memulai ulang Activity yang berjalan (onDestroy() dipanggil, diikuti oleh onCreate()). Perilaku mulai ulang didesain untuk membantu aplikasi Anda beradaptasi dengan konfigurasi baru melalui pemuatan ulang aplikasi Anda secara otomatis dengan sumber daya alternatif sumber yang sesuai dengan konfigurasi perangkat baru.

Untuk menangani mulai ulang dengan benar, aktivitas Anda harus mengembalikan statusnya seperti semula melalui Daur hidup aktivitas normal, dalam hal ini Android akan memanggil onSaveInstanceState() sebelum menghentikan aktivitas Anda sehingga Anda dapat menyimpan data mengenai status aplikasi. Selanjutnya Anda bisa memulihkan statusnya selama onCreate() atau onRestoreInstanceState().

Untuk menguji bahwa aplikasi memulai ulang sendiri dengan status tak berubah, Anda harus memanggil perubahan konfigurasi (seperti mengubah orientasi layar) saat melakukan berbagai tugas dalam aplikasi. Aplikasi Anda harus dapat memulai ulang setiap saat tanpa kehilangan data pengguna atau status untuk menangani kejadian seperti perubahan konfigurasi atau bila pengguna menerima panggilan telepon masuk lalu kembali ke aplikasi setelah proses aplikasi Anda dimusnahkan. Untuk mengetahui cara mengembalikan status aktivitas, bacalah tentang Daur hidup aktivitas.

Akan tetapi, Anda mungkin menemui situasi ketika memulai ulang aplikasi dan mengembalikan data dalam jumlah besar malah menjadi mahal dan menghasilkan pengalaman pengguna yang buruk. Dalam situasi demikian, Anda memiliki dua opsi lain:

  1. Mempertahankan objek selama perubahan konfigurasi

    Izinkan aktivitas Anda memulai ulang saat konfigurasi berubah, namun bawa objek berstatus ke instance baru aktivitas Anda.

  2. Menangani sendiri perubahan konfigurasi

    Cegah sistem memulai ulang aktivitas selama perubahan konfigurasi tertentu, namun terima callback saat konfigurasi benar-benar berubah, agar Anda bisa memperbarui aktivitas secara manual bila diperlukan.

Mempertahankan Objek Selama Perubahan Konfigurasi

Jika memulai ulang aktivitas mengharuskan pemulihan seperangkat data dalam jumlah besar, menghubungkan kembali koneksi jaringan, atau melakukan operasi intensif lainnya, maka mulai ulang penuh karena perubahan konfigurasi mungkin menjadi pengalaman pengguna yang lambat. Selain itu, Anda mungkin tidak bisa sepenuhnya memulihkan status aktivitas dengan Bundle yang disimpan sistem untuk Anda dengan callback onSaveInstanceState()—itu tidaklah didesain untuk membawa objek besar (seperti bitmap) dan data di dalamnya harus diserialkan kemudian dinonserialkan, yang bisa menghabiskan banyak memori dan membuat perubahan konfigurasi menjadi lambat. Dalam situasi demikian, Anda bisa meringankan beban memulai kembali aktivitas Anda dengan mempertahankan Fragment bila aktivitas Anda dimulai ulang karena perubahan konfigurasi. Fragmen ini bisa berisi acuan ke objek stateful yang ingin Anda pertahankan.

Bila sistem Android menghentikan aktivitas Anda karena perubahan konfigurasi, fragmen aktivitas yang telah ditandai untuk dipertahankan tidak akan dimusnahkan. Anda dapat menambahkan fragmen tersebut ke aktivitas untuk mempertahankan objek stateful.

Untuk mempertahankan objek stateful dalam fragmen selama perubahan konfigurasi waktu proses:

  1. Perluas kelas Fragment dan deklarasikan referensi ke objek stateful Anda.
  2. Panggil setRetainInstance(boolean) saat fragmen dibuat.
  3. Tambahkan fragmen ke aktivitas.
  4. Gunakan FragmentManager untuk mengambil fragmen bila aktivitas dimulai ulang.

Misalnya, definisikan fragmen sebagai berikut:

public class RetainedFragment extends Fragment {

    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

Perhatian: Meskipun bisa menyimpan objek apa saja, Anda sama sekali tidak boleh meneruskan objek yang terkait dengan Activity, seperti Drawable, Adapter, View atau objek lainnya yang terkait dengan Context. Jika Anda melakukannya, hal tersebut akan membocorkan semua tampilan dan sumber daya instance aktivitas semula. (Sumber daya yang bocor berarti bahwa aplikasi Anda tetap menyimpannya dan tidak bisa dijadikan kumpulan sampah, sehingga bisa banyak memori yang hilang.)

Kemudian gunakan FragmentManager untuk menambahkan fragmen ke aktivitas. Anda bisa memperoleh objek data dari fragmen saat aktivitas memulai kembali selama perubahan konfigurasi waktu proses. Misalnya, definisikan aktivitas Anda sebagai berikut:

public class MyActivity extends Activity {

    private RetainedFragment dataFragment;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);

        // create the fragment and data the first time
        if (dataFragment == null) {
            // add the fragment
            dataFragment = new DataFragment();
            fm.beginTransaction().add(dataFragment, “data”).commit();
            // load the data from the web
            dataFragment.setData(loadMyData());
        }

        // the data is available in dataFragment.getData()
        ...
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // store the data in the fragment
        dataFragment.setData(collectMyLoadedData());
    }
}

Dalam contoh ini, onCreate() menambahkan fragmen atau memulihkan referensi ke fragmen. onCreate() juga menyimpan objek stateful dalam instance fragmen. onDestroy() akan memperbarui objek stateful dalam instance fragmen yang dipertahankan.

Menangani Sendiri Perubahan Konfigurasi

Jika aplikasi Anda tidak memerlukan pembaruan sumber daya selama perubahan konfigurasi tertentu dan Anda memiliki keterbatasan kinerja yang mengharuskan Anda untuk menghindari mulai ulang aktivitas, maka Anda bisa mendeklarasikan agar aktivitas Anda menangani sendiri perubahan konfigurasinya, sehingga mencegah sistem memulai ulang aktivitas.

Catatan: Menangani sendiri perubahan konfigurasi bisa jauh lebih mempersulit penggunaan sumber daya alternatif, karena sistem tidak menerapkannya secara otomatis untuk Anda. Teknik ini harus dianggap sebagai usaha terakhir bila Anda harus menghindari mulai ulang karena perubahan konfigurasi dan tidak disarankan untuk sebagian besar aplikasi.

Untuk mendeklarasikan agar aktivitas Anda menangani perubahan konfigurasi, edit elemen <activity> yang sesuai dalam file manifes agar menyertakan atribut android:configChanges dengan nilai yang mewakili konfigurasi yang ingin ditangani. Nilai yang memungkinkan tercantum dalam dokumentasi untuk atribut android:configChanges (nilai yang paling sering digunakan adalah "orientation" untuk mencegah mulai ulang bila orientasi layar berubah dan "keyboardHidden" untuk mencegah mulai ulang bila ketersediaan keyboard berubah). Anda bisa mendeklarasikan beberapa nilai konfigurasi dalam atribut dengan memisahkannya menggunakan karakter pipa |.

Misalnya, kode manifes berikut menyatakan aktivitas yang menangani perubahan orientasi layar maupun perubahan ketersediaan keyboard:

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

Sekarang, bila salah satu konfigurasi ini berubah, MyActivity tidak akan dimulai ulang. Sebagai gantinya, MyActivity akan menerima panggilan ke onConfigurationChanged(). Metode ini meneruskan objek Configuration yang menetapkan konfigurasi perangkat baru. Dengan membaca bidang-bidang dalam Configuration, Anda dapat menentukan konfigurasi baru dan membuat perubahan yang sesuai dengan memperbarui sumber daya yang digunakan dalam antarmuka. Pada saat metode ini dipanggil, objek Resources aktivitas Anda akan diperbarui untuk mengembalikan sumber daya berdasarkan konfigurasi baru, jadi Anda bisa dengan mudah menyetel ulang elemen UI tanpa membuat sistem memulai ulang aktivitas Anda.

Perhatian: Mulai Android 3.2 (API level 13), "ukuran layar" juga berubah bila perangkat beralih orientasi antara potret dan lanskap. Jadi jika Anda tidak ingin waktu proses dimulai ulang karena perubahan orientasi saat mengembangkan untuk API level 13 atau yang lebih tinggi (sebagaimana dideklarasikan oleh atribut minSdkVersion dan targetSdkVersion), Anda harus menyertakan nilai "screenSize" selain nilai "orientation". Yaitu, Anda harus mendeklarasikan android:configChanges="orientation|screenSize". Akan tetapi, jika aplikasi Anda menargetkan API level 12 atau yang lebih rendah, maka aktivitas Anda akan selalu menangani sendiri perubahan konfigurasi ini (perubahan konfigurasi ini tidak memulai ulang aktivitas Anda, bahkan saat berjalan pada perangkat Android 3.2 atau yang lebih tinggi).

Misalnya, implementasi onConfigurationChanged() berikut akan memeriksa orientasi perangkat saat ini:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

Objek Configuration mewakili semua konfigurasi saat ini, tidak hanya konfigurasi yang telah berubah. Sering kali Anda tidak perlu memperhatikan dengan persis bagaimana konfigurasi berubah dan cukup menetapkan kembali semua sumber daya yang memberikan alternatif untuk konfigurasi sedang ditangani. Misalnya, karena objek Resources sekarang telah diperbarui, Anda bisa menyetel ulang semua ImageView dengan setImageResource() dan sumber daya yang sesuai untuk konfigurasi baru yang digunakan (seperti yang dijelaskan dalam Menyediakan Sumber Daya).

Perhatikan bahwa nilai-nilai dari bidang Configuration adalah integer yang sesuai dengan konstanta spesifik dari kelas Configuration. Untuk dokumentasi tentang konstanta yang harus digunakan di setiap bidang, lihat bidang yang sesuai dalam referensi Configuration.

Ingatlah: Saat mendeklarasikan aktivitas untuk menangani perubahan konfigurasi, Anda bertanggung jawab untuk menyetel ulang setiap elemen yang alternatifnya Anda berikan. Jika Anda mendeklarasikan aktivitas untuk menangani perubahan orientasi dan memiliki gambar yang harus berubah antara lanskap dan potret, Anda harus menetapkan kembali setiap sumber daya elemen selama onConfigurationChanged().

Jika Anda tidak perlu memperbarui aplikasi berdasarkan perubahan konfigurasi ini, sebagai gantinya Anda bisa saja tidak mengimplementasikan onConfigurationChanged(). Dalam hal ini, semua sumber daya yang digunakan sebelum perubahan konfigurasi akan tetap digunakan dan Anda hanya menghindari mulai ulang aktivitas. Akan tetapi, aplikasi Anda harus selalu bisa dimatikan dan dimulai ulang dengan status sebelumnya tetap utuh, sehingga Anda jangan menganggap teknik ini sebagai jalan keluar untuk mempertahankan status selama daur hidup aktivitas normal. Tidak hanya karena ada perubahan konfigurasi lainnya yang tidak bisa Anda cegah untuk memulai ulang aplikasi, namun juga karena Anda harus menangani kejadian seperti saat pengguna meninggalkan aplikasi dan dimusnahkan sebelum pengguna kembali ke aplikasi.

Untuk informasi selengkapnya tentang perubahan konfigurasi yang bisa Anda tangani dalam aktivitas, lihat dokumentasi android:configChanges dan kelas Configuration.