Bài 4: Activity - Lập trình Android cơ bản

Đăng bởi: Admin | Lượt xem: 5951 | Chuyên mục: Android


1. Activity

1.1 Tìm hiểu Activity
Activity là một thành phần chính của hệ sinh thái Android và là một trong những khái niệm đầu tiên mà bạn cần phải nắm bắt khi bạn trở nên quen thuộc hơn với việc phát triển Android. Các Activity đóng vai trò như một màn hình duy nhất mà sẽ được hiển thị cho người dùng, và chúng thường đóng gói rất nhiều logic của chương trình.
Khi bạn tạo ra một lớp Activity, thủ công hoặc thông qua một trong các mẫu của Android Studio, bạn cần phải xác định nó trong tập tin AndroidManifest.xml của dự án, nếu nó không được thêm vào, như sau:
Trong đoạn code ở trên, bạn cũng thấy một thẻ intent-filter với một action và category. Mặc dù đi sâu vào chi tiết đối với các phần tử này nằm ngoài phạm vi của hướng dẫn này, nhưng bạn nên biết rằng những dòng bổ sung đó là cách hệ thống nhận biết Activity nào để mở trước tiên khi người dùng chọn ứng dụng của bạn.
Một khi bạn đã tạo ra một Activity, bạn có thể cần kết hợp nó với một tập tin Layout XML để bạn có thể dễ dàng sử dụng các đối tượng từ view từ Layout. Bạn có thể làm điều này trong phương thức onCreate().
<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
 
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
Sau khi gọi setContentView(), bạn có thể bắt đầu tham chiếu đến các đối tượng View từ các tập tin Layout XML.
1.2 Vòng đời của Activty
Tại thời điểm này, bạn có thể tự hỏi onCreate() là gì và lý do tại sao chúng ta sử dụng nó. Phương thức onCreate() là một trong những phương thức kết hợp với vòng đời của Activity. Nó được gọi khi Activity lần đầu được tạo ra. Đây là nơi bạn có thể thực một số bước khởi tạo chung trong Activity của bạn vì nó bảo đảm sẽ được gọi trước tiên trong vòng đời Activity như bạn có thể nhìn thấy dưới đây.
Sơ đồ ở trên cho thấy thứ tự của các phương thức được gọi khi một Activity đi ra hoặc đi vào các trạng thái khác nhau có thể:
  1. onCreate() được gọi ngay lập tức khi một Activity được tạo ra, như tên gọi của nó. onDestroy() thì ngược lại và được gọi khi Activity được xoá khỏi bộ nhớ điện thoại. Có một vài trường hợp nhỏ nơi onDestroy() có thể không được gọi, nhưng chúng nằm ngoài phạm vi của hướng dẫn này.
  2. onStart() được gọi khi một Activity hiện hữu đối với người dùng và onStop() được gọi khi mà Activity không còn được nhìn thấy. Cả hai có thể được kích hoạt khi một ứng dụng được đưa vào nền bằng cách sử dụng nút home của thiết bị và khi ứng dụng được đưa trở lại màn hình.
  3. onResume() và onPause() được liên kết với Activity đang ở trên màn hình. Nếu một thành phần khác đi vào màn hình, chẳng hạn như một hộp thoại hoặc một Activity khác, thì phần này của vòng đời được kích hoạt.
Để hiểu rõ hơn về các hàm trong Activity chúng ta cùng đi sang phần thực hành để biết cách thức hoạt động của các hàm.
1.3 Thực hành làm quen với Activity
Trước tiên tạo project như sau :
Tiếp theo chúng ta thêm các hàm trạng thái của Activity để xem khi thay đổi trạng thái chương trình sẽ xử lý như thế nào.
Ở đây mình thêm đủ các hàm onCreate, onRestart, onStop, onPause, onStart, onResume, onDestroy, onSaveInstanceState, onRestoreInstanceState
Và không quên đặt log để xem trạng thái ở cửa sổ logcat.
package com.vncoder.activitylifecycle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Trang thai";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: ");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG, "onRestart: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart: ");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState: ");
    }

    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.d(TAG, "onRestoreInstanceState: ");
    }
}
Kết quả sau khi chạy chương trình :
Bây giờ chúng ta bấm nút home để thoát khỏi ứng dụng.
Tiếp tục mở lại ứng dụng
Tiếp theo chúng ta xoay ngang màn hình.
Tiếp theo hãy thử với phím đa nhiệm.
tiếp tục quay lại với ứng dụng
Cuối cùng chúng ta thoát khỏi ứng dụng.
Việc đặt log để xem tiến trình chạy của Activity rất quan trọng, để tối ưu cũng như hiểu code.
1.4 Làm quen với savedInstanceState
Định nghĩa :
savedInstanceState cũng là một trong các thành phần của trạng thái trong vòng đời của một Activity. Đây là :
  1. Một loại dữ liệu không bền vững.
  2. Không được lưu trữ cụ thể trong đâu ngoài bộ nhớ RAM.
  3. Nó được sử dụng để truyền, phục hồi, lưu trạng thái của một Activity.
  4. Dữ liệu trong savedInstanceState được lưu dưới dạng Bundle.
  5. Được phục hồi khi phương thức onCreate và onRestoreSavedInstanceState được gọi.
  6. Được lưu trước onStop, với phương thức onSaveInstanceState.
Các bạn có biết rằng, một Activity, khi đổi chiều xoay màn hình và Activity đó có hỗ trợ chế độ ngang (landscape) thì cả Activity đó sẽ bị destroy lúc ấy các dữ liệu trong Activity sẽ biến mất lúc đấy savedInstanceState chính là công cụ để giúp bạn giữ được trạng thái của Activity không bị thay đổi khi xoay màn hình.
Ví dụ đơn giản : Bạn viết một app máy tính bỏ túi đơn giản, chỉ có cộng trừ. Sau khi tính xong phép tính 2 + 3=5, bạn xoay màn hình, phép tính biến mất, vậy làm thế nào để kết quả của chúng ta không biến mất ? hãy theo dõi ví dụ của chúng tôi sau đây.
Tạo ứng dụng như hình.
Giao diện XML.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:inputType="number"
        android:id="@+id/etFirstNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:layout_gravity="center"
        android:textSize="20dp"
        android:text="+"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <EditText
        android:inputType="number"
        android:id="@+id/etSecondNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_gravity="center"
        android:id="@+id/sum"
        android:text="Tổng"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:gravity="center"
        android:layout_gravity="center"
        android:hint="="
        android:id="@+id/tvResult"
        android:textSize="40dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>
Code trong MainActivity
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    EditText etFirstNumber,etSecondNumber;
    TextView tvResult;
    Button sum;

    int firstNumber;
    int SecondNumber;
    int Result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etFirstNumber = findViewById(R.id.etFirstNumber);
        etSecondNumber = findViewById(R.id.etSecondNumber);
        tvResult = findViewById(R.id.tvResult);
        sum = findViewById(R.id.sum);

        sum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v == sum){
                    if (etFirstNumber.getText().toString().isEmpty() || etSecondNumber.getText().toString().isEmpty()){
                        Toast.makeText(getBaseContext(),"moi ban nhap so",Toast.LENGTH_LONG).show();
                    }else {
                        firstNumber = Integer.parseInt(etFirstNumber.getText().toString());
                        SecondNumber = Integer.parseInt(etSecondNumber.getText().toString());

                        Result = firstNumber + SecondNumber;

                    tvResult.setText(String.valueOf(Result));
                    }
                }
            }
        });


    }
}
Kết quả :
Mọi thứ vẫn bình thường đến khi xoay ngang màn hình.
Vậy làm sao để khắc phục được điều này. 
Trước tiên bạn cần phải check xem savedInstanceState có null không, nếu null thì tức là nó không có dữ liệu, còn không null thì tức là đã có số được lưu ở trước đó. Và nếu không null thì chúng ta lấy các số đó ra đưa lên hiển thị:
Ở đây chúng ta dùng các key để lấy các giá trị mà muốn chúng không bị thay đổi khi xoay màn hình.
  1. firstNumber là key để lấy ra giá trị của số thứ nhất trong savedInstanceState.
  2. SecondNumber là key để lấy ra giá trị của số thứ hai trong savedInstanceState.
  3. Result là key để lấy ra giá trị của kết quả trong savedInstanceState.
  4. Trong hàm onSaveInstanceState, mình lưu các giá trị đã nhập ở các ô cũng theo đúng 3 key ở trên. Trước khi lưu có kiểm tra xem phép tính đã có kết quả hay chưa.
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    EditText etFirstNumber,etSecondNumber;
    TextView tvResult;
    Button sum;

    int firstNumber;
    int SecondNumber;
    int Result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etFirstNumber = findViewById(R.id.etFirstNumber);
        etSecondNumber = findViewById(R.id.etSecondNumber);
        tvResult = findViewById(R.id.tvResult);
        sum = findViewById(R.id.sum);

        sum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v == sum){
                    if (etFirstNumber.getText().toString().isEmpty() || etSecondNumber.getText().toString().isEmpty()){
                        Toast.makeText(getBaseContext(),"moi ban nhap so",Toast.LENGTH_LONG).show();
                    }else {
                        firstNumber = Integer.parseInt(etFirstNumber.getText().toString());
                        SecondNumber = Integer.parseInt(etSecondNumber.getText().toString());

                        Result = firstNumber + SecondNumber;

                    tvResult.setText(String.valueOf(Result));


                    }
                }
            }
        });
        if (savedInstanceState != null) {

            etFirstNumber.setText(String.valueOf(savedInstanceState.getInt("firstNumber")));

            etSecondNumber.setText(String.valueOf(savedInstanceState.getInt("SecondNumber")));

            tvResult.setText(String.valueOf(savedInstanceState.getInt("Result")));

        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {

        if (!tvResult.getText().toString().isEmpty()) {

            outState.putInt("firstNumber", Integer.parseInt(etFirstNumber.getText().toString()));

            outState.putInt("SecondNumber", Integer.parseInt(etSecondNumber.getText().toString()));

            outState.putInt("Result", Integer.parseInt(tvResult.getText().toString()));

        }
        super.onSaveInstanceState(outState);
    }
}
Kết quả :
Bài viết giới thiệu về Activity, vòng đời của Activity cơ chế của savedInstanceState. hi vọng sau khi đọc xong bài viết các bạn có thể vận dụng được vào các dự án thực tế của mình.
Bài tiếp theo: Fragment >>
vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!