Phần này tạo một ứng dụng đơn giản tên là SaveAndRestoreState
để tìm hiểu việc lưu lại trạng thái của Activity và phục hồi trạng thái của Activity trong vòng đời hoạt động của nó.
Tạo Project - SaveAndRestoreState
Mở Android Studio chọn tạo Project mới, nhập tên ứng dụng là SaveAndRestoreState
Trong cửa sổ tiếp theo chọn Phone and Table và API mong muốn. Bấm tiếp, của sổ Add an Activity to Mobile chọn Empty Activity
Trong của sổ Configure Activity điền tên Activity là: SaveRestoreActivity và layout tên là activity_save_restore như hình dưới. Sau đó Bấm vào Finish
Ta sẽ xây dựng SaveRestoreActivity hiện thị một TextView chứa tên bài học, một nút bấm mà mỗi khi bấm vào
thì tên bài học thay đổi. Khi nút bấm được bấm vào thì màu nền thay đổi từ màu ban đầu sang màu Color.GREEN
như kết quả sau:
Cập nhật lại layout activity_save_restore.xml
thêm TextView và Button với ID tương ứng là message
và nextlesson
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="net.xuanthulab.saveandrestorestate.SaveRetoreActivity"> <TextView android:id="@+id/message" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Bai hoc so: ?" android:padding="10dp" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/next_lesson" android:background="@android:color/holo_blue_dark" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bai hoc tiep" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/message" /> </android.support.constraint.ConstraintLayout>
Tiếp theo cập nhật code Java cho file: SaveRestoreActivity.java
như sau:
public class SaveRetoreActivity extends AppCompatActivity { private int mLesson = 1; //Lưu tên bài hiện tại TextView message; Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_save_retore); //Lấy đối tượng TextView và Button trong layout message = findViewById(R.id.message); button = findViewById(R.id.nextlesson); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //bấm vào thì bài học tăng 1 và đặt //màu nền của nút bấm thành GREEN, mLesson++; setLesson(); button.setBackgroundColor(Color.GREEN); } }); setLession(); } //Hiện thị dòng chữ tên bài hiện tại void setLesson() { message.setText("Bai hoc: " + mLesson); } }
Bạn có thể nhấn Shift + F10
để thạy chử trên máy ảo, ứng dụng hoạt động như kết quả trên hình.
Lưu lại và phục hồi trạng thái trong Activity
Ứng dụng trên hoạt động có vẻ không có vấn đề gì, tuy nhiên khi bấm đến bài số 5, thì màn hình có dòng chữ : Bai học: 5 và nút bấm có nền GREEN. Đang ở trạng thái này, nếu bạn xoay ngang màn hình thì sau khi ứng dụng quay ngang (giả sử trường hợp này điện thoại bật chế độ cho phép xoay ngang) thì sau khi ứng dụng xoay ngang màn hình hiện thị kết quả không như cũ. Dòng chữ trở về nội dung Bai học: 1 và màu nền nút bấm về màu mặc định.
Điều này xảy ra bởi vì trong vòng đời của Activity như chúng ta đã biết, khi Activity từ màn hình đứng quay ngang nó sẽ hủy Activity cũ (onPause -> onDestroy) rồi tạo Activity mới (gọi lại onCreate) do đó mà trạng thái sẽ trở về như lúc khởi tạo ban đầu.
Trường hợp nữa, khi Activiy không còn hiện thị (onPause) nó nằm trong ngăn xếp hệ thống, nó có thể bị hệ thống Android hủy và khởi tạo lại bất kỳ lúc nào để quản lý tài nguyên (bố nhớ), và thế thì khi Activity quay trở lại nó cũng không còn lưu trạng thái như trước khi đi vào onPause.
Vậy làm thế nào để khi Activity khởi tạo lại do hệ thống thay đổi (ví dụ quay ngang màn hình) mà trạng thái của nó vẫn giữ như trạng thái cũ?
Lưu lại trạng thái với onSaveInstanceState
Rất may là Activity có cơ chế để thực hiện việc này (lưu và phục hồi trạng thái). Trước tiên là khi Activity đi vào onPause, nó sẽ tự động gọi phương thức onSaveInstanceState(Bundle outState), ta chỉ việc nạp chồng phương thức đó, và muốn lưu tạm trạng thái nào thì sẽ đưa vào Bundle outState
Ta sẽ nạp chồng(overrided) phương thức này để lưu lại giá trị của mLession
và màu của Button button
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); //Lưu lại giá trị mLession với tên là "lesson" trong outState outState.putInt("lesson", mLesson); //Lấy màn nền button và lưu lại ColorDrawable colorButton = (ColorDrawable) button.getBackground(); outState.putInt("buttoncolor", colorButton.getColor()); }
Bundle outState cho phép lưu dữ liệu trong nó theo cặp tên/giá trị
bằng các phương thức:
putInt(), putString(), putBoolean() ... tùy theo dữ liệu muốn lưu
Lưu ý khi nạp chồng onSaveInstanceState bạn cần gọi trong nó super.onSaveInstanceState(outState);
để các View con trong giao diện cũng có thể lưu và phục hồi trạng thái riêng biệt (vì một số View như TextEdit cũng có cơ chế riêng để lưu
và phục hồi trạng thái).
Phục hồi trạng thái ngay tại onCreate
Sau khi trạng thái (dữ liệu tạm) được lưu như trên, ta có thể đọc được khi Activity hoạt động ở vòng đời mới.
Các giá trị ta lưu ở outState
trong phương thức onSaveInstanceState
sẽ quay trở lại onCreate
trong savedInstanceState
, vậy trong onCreate ta chỉ việc đọc trạng thái trước đây. Ví dụ
phục hồi lại giá trị mLesson
và màu button
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_save_retore); message = findViewById(R.id.message); button = findViewById(R.id.nextlesson); //Phuc hoi if (savedInstanceState != null) { mLession = savedInstanceState.getInt("lesson"); button.setBackgroundColor(savedInstanceState.getInt("buttoncolor")); } setLession(); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mLession++; setLession(); button.setBackgroundColor(Color.GREEN); } }); }
Giờ ứng dụng của chúng ta đã lưu lại trạng thái, khi bạn thử xoay màn hình, sau khi Activiy xoay nó sẽ phục hồi như màn hình trước khi xoay.
Trong đoạn mã trên, luôn cần kiểm tra (savedInstanceState != null) rồi mới đọc giá trị cần phục hồi bởi vì nếu Activiy là khởi đầu mới (không phải kích hoạt lại) thì savedInstanceState = null. Nếu không muốn kiểm tra, có thể không phục hồi ở đây mà tại onRestoreInstanceState
Phục hồi trạng thái tại onRestoreInstanceState(Bundle savedInstanceState)
Mỗi khi OnCreate khởi chạy, nếu có trạng thái được lưu lại thì sau khi gọi onCreate Activity sẽ tự động gọi phương thức onRestoreInstanceState(Bundle savedInstanceState)
,
bạn cũng có thể quá tải phương thức này và đọc dữ liệu cần phục hồi tại savedInstanceState
. Ví dụ:
@Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mLession = savedInstanceState.getInt("lesson"); button.setBackgroundColor(savedInstanceState.getInt("buttoncolor")); setLession(); }
Kết quả cũng tương tự
Sử dụng Bundle
Trong ví dụ trên, đã sử dụng đến đối tượng lớp Bundle để lưu và phục hồi trạng thái. Mục đích của Bundle để truyền dữ liệu giữa các Activity, nó được sử dụng cơ bản bởi Intent
Tạo một đối tượng Bundle
Bundle bundle = new Bundle();
Dữ liệu được đưa vào Bundle khá giống với Map
, tức là dữ liệu nào đó sẽ được lưu theo cặp key
và giá trị
.
Dữ liệu được đọc căn cứ vào key lưu trữ đó
Ví dụ:
bundle.putInt("songuyen", 1000); //key là songuyen int giatri = bundle.getInt("songuyen"); //Đọc
Các phương thức để lưu dữ liệu như: putInt, putBoolean, putString, putFloat ... tương ứng có các phương thức đọc dữ liệu như getInt, getBoolean, getString, getFloat ...