Activity cơ bản (Bài trước)

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à messagenextlesson

<?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 keygiá 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 ...


Đăng ký nhận bài viết mới
Activity cơ bản (Bài trước)