Các hoạt động

Activity là một thành phần ứng dụng cung cấp một màn hình mà với nó người dùng có thể tương tác để thực hiện một điều gì đó, chẳng hạn như quay số điện thoại, chụp ảnh, gửi e-mail hoặc xem bản đồ. Mỗi hoạt động được cho trong một cửa sổ là nơi để vẽ giao diện người dùng của nó. Cửa sổ này thường lấp đầy màn hình, nhưng có thể nhỏ hơn màn hình và nổi bên trên các cửa sổ khác.

Ứng dụng thường bao gồm nhiều hoạt động được liên kết lỏng lẻo với nhau. Thường thì một hoạt động trong một ứng dụng sẽ được quy định là hoạt động "chính", nó được trình bày trước người dùng khi khởi chạy ứng dụng lần đầu. Sau đó, mỗi hoạt động có thể bắt đầu một hoạt động khác để thực hiện các hành động khác nhau. Mỗi khi một hoạt động mới bắt đầu, hoạt động trước đó sẽ bị dừng lại, nhưng hệ thống vẫn giữ nguyên hoạt động trong một ngăn xếp ("back stack"). Khi một hoạt động mới bắt đầu, nó được đẩy lên ngăn xếp và chiếm lấy tiêu điểm của người dùng. Ngăn xếp sẽ tuân theo cơ chế xếp chồng cơ bản "vào cuối, ra đầu", vì thế, khi người dùng kết thúc hoạt động hiện tại và nhấn nút Quay lại, nó sẽ được đẩy ra khỏi ngăn xếp (và bị hủy) và hoạt động trước đó sẽ tiếp tục. (Ngăn xếp được đề cập kỹ hơn trong tài liệu Tác vụ và Ngăn Xếp.)

Khi một hoạt động bị dừng vì một hoạt động mới bắt đầu, nó được thông báo về sự thay đổi trạng thái này qua các phương pháp gọi lại vòng đời của hoạt động. Có một vài phương pháp gọi lại vòng đời mà một hoạt động có thể nhận, do một thay đổi về trạng thái của nó—dù hệ thống đang tạo, dừng hay tiếp tục nó, hay hủy nó—và mỗi lần gọi lại cho bạn cơ hội thực hiện công việc cụ thể phù hợp với sự thay đổi trạng thái đó. Ví dụ, khi bị dừng, hoạt động của bạn sẽ giải phóng mọi đối tượng lớn, chẳng hạn như các kết nối mạng hoặc cơ sở dữ liệu. Khi hoạt động tiếp tục, bạn có thể thu lại những tài nguyên cần thiết và tiếp tục những hành động bị gián đoạn. Những chuyển tiếp trạng thái này đều là một phần của vòng đời hoạt động.

Phần còn lại của tài liệu này bàn đến những nội dung cơ bản về cách xây dựng và sử dụng một hoạt động, bao gồm một nội dung đề cập đầy đủ về cách vận hành của vòng đời hoạt động, để bạn có thể quản lý tốt sự chuyển tiếp giữa các trạng thái hoạt động khác nhau.

Tạo một Hoạt động

Để tạo một hoạt động, bạn phải tạo một lớp con của Activity (hoặc một lớp con hiện tại của nó). Trong lớp con của mình, bạn cần triển khai các phương pháp gọi lại mà hệ thống gọi khi hoạt động chuyển tiếp giữa các trạng thái khác nhau trong vòng đời, chẳng hạn như khi hoạt động đang được tạo, dừng, tiếp tục, hoặc hủy. Hai phương pháp gọi lại quan trọng nhất là:

onCreate()
Bạn phải triển khai phương pháp này. Hệ thống gọi phương pháp này khi tạo hoạt động của bạn. Trong quá trình thực hiện của mình, bạn nên khởi chạy những thành phần thiết yếu cho hoạt động của mình. Quan trọng nhất, đây là lúc bạn phải gọi setContentView() để định nghĩa bố trí cho giao diện người dùng của hoạt động.
onPause()
Hệ thống gọi phương pháp này là dấu hiệu đầu tiên về việc người dùng đang rời khỏi hoạt động của bạn (mặc dù không phải lúc nào cũng có nghĩa rằng hoạt động đang bị hủy). Trường hợp này thường là khi bạn định thực hiện bất kỳ thay đổi nào vẫn cần có hiệu lực ngoài phiên của người dùng hiện thời (vì người dùng có thể không quay lại).

Có một vài phương pháp gọi lại vòng đời khác mà bạn nên sử dụng để đem đến một trải nghiệm người dùng mượt mà giữa các hoạt động và xử lý những gián đoạn bất ngờ khiến hoạt động của bạn bị dừng và thậm chí bị hủy. Tất cả phương pháp gọi lại vòng đời được bàn sau trong phần nói về Quản lý Vòng đời của Hoạt động.

Triển khai một giao diện người dùng

Giao diện người dùng cho một hoạt động sẽ được cung cấp theo phân cấp dạng xem—đối tượng được suy ra từ lớp View. Mỗi chế độ xem kiểm soát một không gian chữ nhật riêng trong cửa sổ của hoạt động và có thể phản hồi trước tương tác của người dùng. Ví dụ, chế độ xem có thể là một nút khởi xướng một hành động khi người dùng chạm vào nó.

Android cung cấp nhiều chế độ xem sẵn có mà bạn có thể sử dụng để thiết kế và tổ chức cho bố trí của mình. "Widget" là những chế độ xem cung cấp những phần tử trực quan (và tương tác) cho màn hình, chẳng hạn như nút, trường văn bản, hộp kiểm, hay chỉ là một hình ảnh. "Bố trí" là những chế độ xem được suy ra từ ViewGroup cung cấp một mô hình bố trí duy nhất cho các chế độ xem con của nó, chẳng hạn như bố trí tuyến tính, bố trí lưới, hoặc bố trí tương đối. Bạn cũng có thể chia thành lớp con View và các lớp ViewGroup (hoặc các lớp con hiện tại) để tạo widget và bố trí của chính mình và áp dụng chúng vào bố trí hoạt động của bạn.

Cách phổ biến nhất để định nghĩa một bố trí bằng cách sử dụng các chế độ xem là dùng một tệp bố trí XML được lưu trong tài nguyên ứng dụng của bạn. Bằng cách này, bạn có thể duy trì thiết kế giao diện người dùng của mình độc lập với mã nguồn định nghĩa hành vi của hoạt động. Bạn có thể đặt bố trí làm UI cho hoạt động của mình bằng setContentView(), chuyển ID tài nguyên cho bố trí. Tuy nhiên, bạn cũng có thể tạo View mới trong mã hoạt động của mình và xây dựng một cấp bậc chế độ xem bằng cách chèn các View mới vào một ViewGroup, sau đó sử dụng bố trí đó bằng cách chuyển root ViewGroup sang setContentView().

Để biết thông tin về việc tạo một giao diện người dùng, hãy xem tài liệu Giao diện Người dùng.

Khai báo hoạt động trong bản kê khai

Bạn phải khai báo hoạt động của mình trong tệp bản kê khai để hoạt động có thể truy cập được vào hệ thống. Để khai báo hoạt động của mình, hãy mở tệp bản kê khai của bạn và thêm một phần tử <activity> làm con của phần tử <application> . Ví dụ:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

Có vài thuộc tính khác mà bạn có thể nêu trong phần tử này, để định nghĩa các thuộc tính như nhãn cho hoạt động, biểu tượng cho hoạt động, hoặc chủ đề mô tả kiểu UI của hoạt động. Thuộc tính android:name là thuộc tính bắt buộc duy nhất—nó quy định tên lớp của hoạt động. Một khi bạn phát hành ứng dụng của mình, bạn không nên thay đổi tên này, vì nếu bạn làm vậy, bạn có thể làm hỏng một số tính năng, chẳng hạn như các lối tắt của ứng dụng (hãy đọc bài đăng trên blog, Những Điều Không Thay Đổi Được).

Xem tài liệu tham khảo phần tử &lt;activity&gt; để biết thêm thông tin về việc khai báo hoạt động của bạn trong bản kê khai.

Sử dụng các bộ lọc ý định

Một phần tử &lt;activity&gt; cũng có thể quy định các bộ lọc ý định khác nhau—bằng cách sử dụng phần tử &lt;intent-filter&gt; —để khai báo cách thức mà các thành phần khác của ứng dụng có thể kích hoạt nó.

Khi bạn tạo một ứng dụng mới bằng cách sử dụng các công cụ SDK của Android, hoạt động chương trình nhỏ được tạo cho bạn sẽ tự động bao gồm một bộ lọc ý định khai báo hoạt động phản hồi lại hành động "chính" và nên được đặt trong thể loại "trình khởi chạy". Bộ lọc ý định trông như thế này:

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Phần tử &lt;action&gt; quy định rằng đây là điểm mục nhập "chính" đối với ứng dụng. Phần tử &lt;category&gt; quy định rằng hoạt động này nên được liệt kê trong trình khởi chạy ứng dụng của hệ thống (để cho phép người dùng khởi chạy hoạt động này).

Nếu bạn có ý định cho ứng dụng của mình được độc lập và không cho phép các ứng dụng khác kích hoạt các hoạt động của nó, vậy bạn không cần bất kỳ bộ lọc ý định nào khác. Chỉ một hoạt động nên có hành động "chính" và thể loại "trình khởi chạy" như trong ví dụ trước. Những hoạt động mà bạn không muốn cung cấp sẵn cho các ứng dụng khác không nên có bộ lọc ý định và bạn có thể tự mình bắt đầu chúng bằng cách sử dụng các ý định rõ ràng (như được đề cập trong phần sau).

Tuy nhiên, nếu bạn muốn hoạt động của mình phản hồi lại những ý định ngầm mà được chuyển giao từ các ứng dụng khác (và chính bạn), thì bạn phải định nghĩa các bộ lọc ý định bổ sung cho hoạt động của mình. Với mỗi loại ý định mà bạn muốn phản hồi, bạn phải nêu một &lt;intent-filter&gt; bao gồm một phần tử &lt;action&gt; và, không bắt buộc, một phần tử &lt;category&gt; và/hoặc một phần tử &lt;data&gt;. Những phần tử này quy định loại ý định mà hoạt động của bạn có thể phản hồi.

Để biết thêm thông tin về cách thức các hoạt động của bạn có thể phản hồi lại ý định, hãy xem tài liệu Ý định và Bộ lọc Ý định .

Bắt đầu một Hoạt động

Bạn có thể bắt đầu một hoạt động khác bằng cách gọi startActivity(), chuyển cho nó một Intent mà mô tả hoạt động bạn muốn bắt đầu. Ý định này sẽ quy định hoặc hoạt động chính xác mà bạn muốn bắt đầu hoặc mô tả loại hành động mà bạn muốn thực hiện (và hệ thống lựa chọn hoạt động phù hợp cho bạn, thậm chí có thể từ một ứng dụng khác). Một ý định cũng có thể mang theo lượng nhỏ dữ liệu sẽ được sử dụng bởi hoạt động được bắt đầu.

Khi đang làm việc trong ứng dụng của chính mình, bạn thường sẽ cần khởi chạy một hoạt động đã biết. Bạn có thể làm vậy bằng cách tạo một ý định trong đó quy định rõ hoạt động bạn muốn bắt đầu, sử dụng tên lớp đó. Ví dụ, sau đây là cách một hoạt động bắt đầu một hoạt động khác có tên SignInActivity:

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

Tuy nhiên, ứng dụng của bạn cũng có thể muốn thực hiện một số hành động, chẳng hạn như gửi một e-mail, tin nhắn văn bản, hoặc cập nhật trạng thái, bằng cách sử dụng dữ liệu từ hoạt động của bạn. Trong trường hợp này, ứng dụng của bạn có thể không có các hoạt động của chính nó để thực hiện những hành động đó, vì vậy, thay vào đó, bạn có thể tận dụng những hoạt động được cung cấp bởi các ứng dụng khác trên thiết bị mà có thể thực hiện hành động cho bạn. Đây là lúc ý định thực sự có giá trị—bạn có thể tạo một ý định mô tả một hành động bạn muốn thực hiện và hệ thống sẽ khởi chạy hoạt động phù hợp đó từ một ứng dụng khác. Nếu có nhiều hoạt động mà có thể xử lý ý định, vậy người dùng có thể chọn hoạt động nào sẽ sử dụng. Ví dụ, nếu bạn muốn cho phép người dùng gửi e-mail, bạn có thể tạo ý định sau:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

EXTRA_EMAIL phụ được thêm vào ý định là một mảng xâu của các địa chỉ e-mail mà e-mail sẽ được gửi tới. Khi một ứng dụng e-mail phản hồi ý định này, nó đọc mảng xâu được cung cấp trong phần phụ và đặt nó vào trường "đến" của mẫu soạn thảo e-mail. Trong trường hợp này, hoạt động của ứng dụng e-mail bắt đầu và khi người dùng làm xong, hoạt động của bạn sẽ tiếp tục.

Bắt đầu một hoạt động cho một kết quả

Đôi khi bạn có thể muốn nhận được một kết quả từ hoạt động mà bạn bắt đầu. Trong trường hợp đó, hãy bắt đầu hoạt động bằng cách gọi startActivityForResult() (thay vì startActivity()). Rồi để nhận được kết quả từ hoạt động sau đó, hãy triển khai phương pháp gọi lại onActivityResult() . Khi hoạt động sau đó diễn ra xong, nó trả về một kết quả trong một Intent cho phương pháp onActivityResult() của bạn.

Ví dụ, bạn có thể muốn người dùng chọn một trong các liên lạc của họ, vì vậy hoạt động của bạn có thể làm gì đó với thông tin trong liên lạc đó. Đây là cách bạn có thể tạo một ý định như vậy và xử lý kết quả:

private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

Ví dụ này thể hiện lô-gic cơ bản mà bạn sẽ sử dụng trong phương pháp onActivityResult() của mình để xử lý một kết quả hoạt động. Điều kiện đầu tiên kiểm tra xem yêu cầu có thành công không—nếu có thì resultCode sẽ là RESULT_OK—và liệu yêu cầu mà kiểm tra này đang phản hồi có được biết hay không—trong trường hợp này, requestCode phù hợp với tham số thứ hai được gửi bằng startActivityForResult(). Từ đó, mã xử lý kết quả hoạt động bằng cách truy vấn dữ liệu được trả về trong Intent (tham số data).

Điều xảy ra đó là, ContentResolver sẽ thực hiện một truy vấn đối với nhà cung cấp nội dung, truy vấn này trả về một Cursor cho phép đọc dữ liệu được truy vấn. Để biết thêm thông tin, hãy xem tài liệu Trình cung cấp Nội dung.

Để biết thêm thông tin về việc sử dụng ý định, hãy xem tài liệu Ý định và Bộ lọc Ý định.

Tắt một Hoạt động

Bạn có thể tắt một hoạt động bằng cách gọi phương pháp finish() của nó. Bạn cũng có thể tắt một hoạt động riêng mà trước đó bạn đã bắt đầu bằng cách gọi finishActivity().

Lưu ý: Trong hầu hết trường hợp, bạn không nên kết thúc một hoạt động một cách rõ ràng bằng cách sử dụng những phương pháp này. Như đề cập trong phần sau về vòng đời của hoạt động, hệ thống Android quản lý tuổi thọ của một hoạt động cho bạn, vì vậy bạn không cần kết thúc các hoạt động của chính mình. Việc gọi những phương pháp này có thể ảnh hưởng tiêu cực tới trải nghiệm người dùng kỳ vọng và chỉ nên được sử dụng khi bạn tuyệt đối không muốn người dùng quay lại thực thể này của hoạt động.

Quản lý Vòng đời của Hoạt động

Việc quản lý vòng đời các hoạt động của bạn bằng cách triển khai các phương pháp gọi lại rất quan trọng đối với việc xây dựng một ứng dụng mạnh và linh hoạt. Vòng đời của một hoạt động trực tiếp bị ảnh hưởng bởi sự liên kết giữa nó với các hoạt động khác, tác vụ của nó và ngăn xếp (back stack).

Về cơ bản, một hoạt động có thể tồn tại ở ba trạng thái:

Tiếp tục
Hoạt động ở tiền cảnh của màn hình và có tiêu điểm của người dùng. (Trạng thái này đôi khi cũng được gọi là "đang chạy".)
Tạm dừng
Một hoạt động khác ở tiền cảnh và có tiêu điểm, nhưng hoạt động này vẫn hiển thị. Cụ thể, một hoạt động khác hiển thị ở trên hoạt động này và hoạt động đó trong suốt một phần hoặc không che toàn bộ màn hình. Trạng thái tạm dừng hoàn toàn đang hoạt động (đối tượng Activity được giữ lại trong bộ nhớ, nó duy trì tất cả thông tin về trạng thái và thành viên, và vẫn gắn với trình quản lý cửa sổ), nhưng có thể bị hệ thống tắt bỏ trong trường hợp bộ nhớ cực kỳ thấp.
Dừng
Hoạt động bị che khuất hoàn toàn bởi một hoạt động khác (hoạt động hiện đang “dưới nền"). Hoạt động dừng cũng vẫn đang hoạt động (Activity đối tượng được giữ lại trong bộ nhớ, nó duy trì tất cả thông tin về trạng thái và thành viên, nhưng không gắn với trình quản lý cửa sổ). Tuy nhiên, hoạt động không còn hiển thị với người dùng nữa và hệ thống có thể tắt bỏ hoạt động này khi cần bộ nhớ ở nơi khác.

Nếu một hoạt động bị tạm dừng hoặc dừng, hệ thống có thể bỏ nó khỏi bộ nhớ hoặc bằng cách yêu cầu nó kết thúc (gọi phương pháp finish() của nó), hoặc đơn giản là tắt bỏ tiến trình của hoạt động. Khi hoạt động được mở lại (sau khi bị kết thúc hoặc tắt bỏ), nó phải được tạo lại hoàn toàn.

Triển khai gọi lại vòng đời

Khi một hoạt động chuyển tiếp vào ra các trạng thái khác nhau nêu trên, nó được thông báo thông qua các phương pháp gọi lại. Tất cả phương pháp gọi lại đều là những móc (hook) mà bạn có thể khống chế để làm công việc phù hợp khi trạng thái hoạt động của bạn thay đổi. Hoạt động khung sau bao gồm từng phương pháp trong các phương pháp vòng đời cơ bản:

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}

Lưu ý: Việc bạn triển khai những phương pháp vòng đời này phải luôn gọi triển khai siêu lớp trước khi làm bất kỳ công việc nào, như minh họa trong các ví dụ bên trên.

Cùng nhau, những phương pháp này định nghĩa toàn bộ vòng đời của một hoạt động. Bằng việc triển khai những phương pháp này, bạn có thể theo dõi ba vòng lặp lồng nhau trong vòng đời của hoạt động:

  • Toàn bộ vòng đời của một hoạt động sẽ xảy ra từ thời điểm lệnh gọi đến onCreate() cho tới thời điểm lệnh gọi đến onDestroy(). Hoạt động của bạn nên thực hiện thiết lập trạng thái "chung" (chẳng hạn như định nghĩa bố trí) trong onCreate(), và giải phóng tất cả tài nguyên còn lại trong onDestroy(). Ví dụ, nếu hoạt động của bạn có một luồng đang chạy ngầm để tải xuống dữ liệu từ mạng, nó có thể tạo luồng đó trong onCreate() rồi dừng luồng trong onDestroy().
  • Vòng đời hiển thị của một hoạt động xảy ra từ thời điểm lệnh gọi đến onStart() cho tới lệnh gọi đến onStop(). Trong thời gian này, người dùng có thể thấy hoạt động trên màn hình và tương tác với nó. Ví dụ, onStop() được gọi khi một hoạt động mới bắt đầu và không còn hiển thị nữa. Giữa hai phương pháp này, bạn có thể duy trì các tài nguyên cần để cho người dùng thấy hoạt động. Ví dụ, bạn có thể đăng ký một BroadcastReceiver trong onStart() để theo dõi các thay đổi tác động tới UI của mình, và bỏ đăng ký nó trong onStop() khi người dùng không còn thấy thứ bạn đang hiển thị nữa. Hệ thống có thể gọi onStart()onStop() nhiều lần trong suốt vòng đời của hoạt động, khi đó hoạt động luân chuyển giữa trạng thái hiển thị và ẩn với người dùng.

  • Vòng đời ở tiền cảnh của một hoạt động xảy ra từ thời điểm lệnh gọi đến onResume() cho tới thời điểm lệnh gọi đến onPause(). Trong thời gian này, hoạt động sẽ ở phía trước tất cả hoạt động khác trên màn hình và có tiêu điểm đầu vào của người dùng. Hoạt động có thể thường xuyên chuyển tiếp vào và ra tiền cảnh—ví dụ, onPause() được gọi khi thiết bị vào trạng thái ngủ hoặc khi một hộp thoại xuất hiện. Vì trạng thái này có thể chuyển tiếp thường xuyên, mã trong hai phương pháp này nên tương đối nhẹ để tránh chuyển tiếp chậm khiến người dùng phải đợi.

Hình 1 minh họa những vòng lặp này và các đường dẫn mà một hoạt động có thể diễn ra giữa các trạng thái. Hình chữ nhật đại diện cho các phương pháp gọi lại bạn có thể triển khai để thực hiện thao tác khi hoạt động chuyển tiếp giữa những trạng thái này.

Hình 1. Vòng đời của hoạt động.

Những phương pháp gọi lại vòng đời này cũng được liệt kê trong bảng 1, trong đó mô tả từng phương pháp gọi lại một cách chi tiết hơn và xác định từng phương pháp trong vòng đời tổng thể của hoạt động, bao gồm việc hệ thống có thể tắt bỏ hoạt động hay không sau khi phương pháp gọi lại hoàn tất.

Bảng 1. Tóm tắt các phương pháp gọi lại trong vòng đời của hoạt động.

Phương pháp Mô tả Có thể tắt bỏ sau? Tiếp theo
onCreate() Được gọi khi hoạt động mới được tạo. Đây là lúc bạn nên thực hiện tất cả thiết lập cố định thông thường của mình — tạo chế độ xem, kết ghép dữ liệu với danh sách, v.v. Phương pháp này được chuyển cho một đối tượng Gói chứa trạng thái trước đây của hoạt động, nếu trạng thái đó được thu lại (xem phần Lưu Trạng thái Hoạt động, ở đoạn sau).

Luôn được theo sau bởi onStart().

Không onStart()
     onRestart() Được gọi sau khi hoạt động đã được dừng, ngay trước khi hoạt động được bắt đầu lại.

Luôn được theo sau bởi onStart()

Không onStart()
onStart() Được gọi ngay trước khi hoạt động hiển thị trước người dùng.

Được theo sau bởi onResume() nếu hoạt động vào tiền cảnh, hoặc onStop() nếu hoạt động bị ẩn.

Không onResume()
hoặc
onStop()
     onResume() Được gọi ngay trước khi hoạt động bắt đầu tương tác với người dùng. Tại điểm này, hoạt động nằm ở trên cùng của chồng hoạt động, trong đó mục nhập của người dùng sẽ đến hoạt động này.

Luôn được theo sau bởi onPause().

Không onPause()
onPause() Được gọi khi hệ thống sắp bắt đầu tiếp tục một hoạt động khác. Phương pháp này thường được sử dụng để thực hiện các thay đổi chưa lưu cho dữ liệu liên tục, dừng các hoạt ảnh và những việc khác mà có thể tiêu tốn công suất CPU, v.v. Nó sẽ thực hiện rất nhanh, vì hoạt động tiếp theo sẽ không được tiếp tục tới khi nó trở lại.

Được theo sau hoặc bởi onResume() nếu hoạt động trở lại phía trước, hoặc bởi onStop() nếu nó không hiển thị với người dùng.

onResume()
hoặc
onStop()
onStop() Được gọi khi hoạt động không còn hiển thị với người dùng. Điều này có thể xảy ra vì nó đang bị hủy, hoặc vì một hoạt động khác (đang tồn tại hoặc mới) đã được tiếp tục và đang che khuất nó.

Được theo sau hoặc bởi onRestart() nếu hoạt động đang quay lại để tương tác với người dùng, hoặc bởi onDestroy() nếu hoạt động này sẽ đi mất.

onRestart()
hoặc
onDestroy()
onDestroy() Được gọi trước khi hoạt động bị hủy. Đây là lần gọi cuối cùng mà hoạt động sẽ nhận được. Nên gọi nó hoặc vì hoạt động đang kết thúc (ai đó đã gọi finish() trên nó), hoặc vì hệ thống đang tạm thời hủy thực thể này của hoạt động để tiết kiệm bộ nhớ trống. Bạn có thể phân biệt những những kịch bản này bằng phương pháp isFinishing(). không có gì

Cột ghi "Có thể tắt bỏ sau?" cho biết liệu hệ thống có thể tắt bỏ tiến trình đang lưu trữ hoạt động vào bất cứ lúc nào sau khi phương pháp trả về, mà không thực hiện một dòng mã khác của hoạt động hay không. Ba phương pháp được ghi là "có": (onPause(), onStop(), và onDestroy()). Vì onPause() là phương pháp đầu tiên trong ba phương pháp, sau khi hoạt động được tạo, onPause() là phương pháp cuối cùng được bảo đảm sẽ được gọi trước khi tiến trình có thể bị tắt bỏ—nếu hệ thống phải khôi phục bộ nhớ trong một tình huống khẩn cấp, khi đó onStop()onDestroy() có thể không được gọi. Vì thế, bạn nên sử dụng onPause() để ghi dữ liệu cố định quan trọng (chẳng hạn như những chỉnh sửa của người dùng) vào thiết bị lưu trữ. Tuy nhiên, bạn nên chọn lọc thông tin nào phải được giữ lại trong onPause(), vì bất kỳ thủ tục chặn nào trong phương pháp này cũng chặn chuyển tiếp sang hoạt động kế tiếp và làm chậm trải nghiệm của người dùng.

Những phương pháp được ghi "Không" trong cột Có thể tắt bỏ sẽ bảo vệ tiến trình đang lưu trữ hoạt động khỏi bị tắt bỏ từ thời điểm chúng được gọi. Vì thế, một hoạt động có thể tắt bỏ được từ thời điểm onPause() trở về tới thời điểm onResume() sẽ được gọi. Nó sẽ không thể lại tắt bỏ được tới khi onPause() lại được gọi và trả về.

Lưu ý: Một hoạt động mà không thể "tắt bỏ được" về mặt kỹ thuật bởi định nghĩa này trong bảng 1 vẫn có thể bị hệ thống tắt bỏ—nhưng điều đó chỉ xảy ra trong những hoàn cảnh cực đoan khi không còn giải pháp nào khác. Thời điểm một hoạt động có thể bị tắt bỏ được đề cập kỹ hơn trong tài liệu Tiến trình và Luồng.

Lưu trạng thái của hoạt động

Phần giới thiệu về Quản lý Vòng đời của Hoạt động có đề cập sơ qua rằng khi một hoạt động bị tạm dừng hoặc dừng, trạng thái của hoạt động đó sẽ được giữ lại. Điều này đúng vì đối tượng Activity vẫn được giữ trong bộ nhớ khi nó bị tạm dừng hoặc dừng—tất cả thông tin về các thành viên và trạng thái hiện tại của nó vẫn hoạt động. Vì thế, bất kỳ thay đổi nào mà người dùng đã thực hiện trong hoạt động đều được giữ lại sao cho khi hoạt động trở về tiền cảnh (khi nó "tiếp tục"), thì những thay đổi này vẫn còn đó.

Tuy nhiên, khi hệ thống hủy một hoạt động để khôi phục bộ nhớ, đối tượng Activity bị hủy, vì vậy hệ thống không thể đơn thuần tiếp tục hoạt động với trạng thái không bị ảnh hưởng. Thay vào đó, hệ thống phải tạo lại đối tượng Activity nếu người dùng điều hướng trở lại nó. Tuy vậy, người dùng không biết rằng hệ thống đã hủy hoạt động và tạo lại nó và, vì thế, có thể cho rằng hoạt động sẽ vẫn nguyên như cũ. Trong tình huống này, bạn có thể đảm bảo rằng thông tin quan trọng về trạng thái của hoạt động được giữ nguyên bằng cách triển khai một phương pháp gọi lại bổ sung cho phép bạn lưu thông tin về trạng thái của hoạt động của mình: onSaveInstanceState().

Hệ thống gọi onSaveInstanceState() trước khi khiến hoạt động dễ bị hủy. Hệ thống chuyển cho phương pháp này một Bundle trong đó bạn có thể lưu thông tin trạng thái về hoạt động như cặp tên giá trị, bằng cách sử dụng các phương pháp như putString()putInt(). Sau đó, nếu hệ thống tắt bỏ tiến trình ứng dụng của bạn và người dùng điều hướng trở lại hoạt động của bạn, hệ thống sẽ tạo lại hoạt động đó và chuyển Bundle cho cả onCreate()onRestoreInstanceState(). Sử dụng một trong hai phương pháp này, bạn có thể trích xuất trạng thái đã lưu của mình từ Bundle và khôi phục trạng thái của hoạt động. Nếu không có thông tin trạng thái để khôi phục, khi đó Bundle được chuyển cho bạn sẽ rỗng (là trường hợp khi hoạt động được tạo lần đầu).

Hình 2. Hai cách mà theo đó một hoạt động trở về tiêu điểm của người dùng với trạng thái không thay đổi: hoặc hoạt động bị hủy, rồi tạo lại và hoạt động phải khôi phục trạng thái đã lưu trước đó, hoặc hoạt động bị dừng, rồi tiếp tục và trạng thái của hoạt động giữ nguyên không đổi.

Lưu ý: Không có gì bảo đảm rằng onSaveInstanceState() sẽ được gọi trước khi hoạt động của bạn bị hủy, vì có những trường hợp mà sẽ không cần lưu trạng thái (chẳng hạn như khi người dùng rời bỏ hoạt động của bạn bằng cách sử dụng nút Quay lại, vì người dùng rõ ràng đang đóng hoạt động). Nếu hệ thống gọi onSaveInstanceState(), nó làm vậy trước onStop() và có thể trước cả onPause().

Tuy nhiên, ngay cả khi bạn không làm gì và không triển khai onSaveInstanceState(), một phần trạng thái của hoạt động được khôi phục bởi việc lớp Activity triển khai mặc định onSaveInstanceState(). Cụ thể, triển khai mặc định sẽ gọi phương pháp onSaveInstanceState() tương ứng cho mọi View trong bố trí, nó cho phép mỗi chế độ xem cung cấp thông tin về chính nó mà sẽ được lưu. Gần như mọi widget trong khuôn khổ Android đều triển khai phương pháp này nếu phù hợp, sao cho mọi thay đổi hiển thị đối với UI đều tự động được lưu và khôi phục khi hoạt động của bạn được tạo lại. Ví dụ, widget EditText lưu mọi văn bản do người dùng điền vào và widget CheckBox lưu sẽ thông tin cho dù đã được kiểm tra hay chưa. Việc duy nhất bạn cần làm đó là cung cấp một ID duy nhất (với thuộc tính android:id ) cho mỗi widget bạn muốn lưu trạng thái của nó. Nếu một widget không có ID thì hệ thống không thể lưu trạng thái của nó.

Mặc dù việc triển khai mặc định onSaveInstanceState() lưu thông tin hữu ích về UI hoạt động của bạn, bạn có thể vẫn cần khống chế nó để lưu thêm thông tin. Ví dụ, bạn có thể cần lưu các giá trị thành viên đã thay đổi trong vòng đời của hoạt động (mà có thể tương quan với các giá trị được khôi phục trong UI, nhưng các thành viên nắm giữ giá trị UI đó không được khôi phục theo mặc định).

Vì việc triển khai mặc định onSaveInstanceState() giúp lưu trạng thái của UI, nếu bạn khống chế phương pháp để lưu thêm thông tin trạng thái, bạn nên luôn luôn gọi triển khai siêu lớp của onSaveInstanceState() trước khi thực hiện bất kỳ công việc nào. Tương tự, bạn cũng nên gọi triển khai siêu lớp onRestoreInstanceState() nếu bạn khống chế nó, để triển khai mặc định có thể khôi phục các trạng thái xem.

Lưu ý:onSaveInstanceState() không đảm bảo sẽ được gọi, bạn chỉ nên sử dụng nó để ghi trạng thái giao thời của hoạt động (trạng thái của UI)—bạn không nên sử dụng nó để lưu giữ dữ liệu liên tục. Thay vào đó, bạn nên sử dụng onPause() để lưu giữ dữ liệu liên tục (chẳng hạn như dữ liệu mà nên được lưu vào một cơ sở dữ liệu) khi người dùng rời bỏ hoạt động.

Một cách hay để kiểm tra khả năng khôi phục trạng thái của ứng dụng của bạn đó là chỉ cần xoay thiết bị sao cho hướng màn hình thay đổi. Khi hướng màn hình thay đổi, hệ thống hủy và tạo lại hoạt động để áp dụng các tài nguyên thay thế mà có thể có sẵn cho cấu hình màn hình mới. Chỉ với lý do này mà một điều rất quan trọng đó là hoạt động của bạn hoàn toàn khôi phục trạng thái của mình khi nó được tạo lại, vì người dùng thường xoay màn hình trong khi sử dụng ứng dụng.

Xử lý thay đổi về cấu hình

Một số cấu hình thiết bị có thể thay đổi trong thời gian chạy (chẳng hạn như hướng màn hình, sự sẵn có của bàn phím, và ngôn ngữ). Khi sự thay đổi đó diễn ra, Android tạo lại hoạt động đang chạy (hệ thống gọi onDestroy(), rồi ngay lập tức gọi onCreate()). Hành vi này được thiết kế để giúp ứng dụng của bạn điều chỉnh theo những cấu hình mới bằng cách tự động tải lại ứng dụng của bạn bằng các tài nguyên thay thế mà bạn đã cung cấp (chẳng hạn như bố trí khác cho các hướng và kích cỡ màn hình khác).

Nếu bạn thiết kế hoạt động của mình một cách phù hợp để xử lý khởi động lại do thay đổi hướng màn hình và khôi phục trạng thái hoạt động như nêu trên, ứng dụng của bạn sẽ linh hoạt hơn trước những sự kiện bất ngờ khác trong vòng đời của hoạt động.

Cách tốt nhất để xử lý khởi động lại đó là lưu và khôi phục trạng thái hoạt động của bạn bằng cách sử dụng onSaveInstanceState()onRestoreInstanceState() (hoặc onCreate()), như đã đề cập trong phần trước.

Để biết thêm thông tin về những thay đổi cấu hình xảy ra tại thời điểm chạy và cách bạn có thể xử lý chúng, hãy đọc hướng dẫn Xử lý Thay đổi trong Thời gian chạy.

Điều phối hoạt động

Khi một hoạt động bắt đầu một hoạt động khác, cả hai đều trải qua những chuyển tiếp vòng đời. Hoạt động thứ nhất tạm dừng và dừng (tuy nhiên, nó sẽ không dừng nếu vẫn hiển thị được dưới nền), trong khi hoạt động kia được tạo. Trong trường hợp những hoạt động này chia sẻ dữ liệu được lưu vào đĩa hoặc nơi khác, điều quan trọng là phải hiểu rằng hoạt động thứ nhất không bị dừng hoàn toàn trước khi hoạt động thứ hai được tạo. Thay vào đó, tiến trình bắt đầu hoạt động thứ hai chồng lấp với tiến trình dừng hoạt động thứ nhất.

Thứ tự gọi lại vòng đời được định nghĩa rõ, cụ thể là khi hai hoạt động trong cùng tiến trình và hoạt động này bắt đầu hoạt động kia. Sau đây là thứ tự thao tác diễn ra khi Hoạt động A bắt đầu Hoạt động B:

  1. Phương pháp onPause() của Hoạt động A thực thi.
  2. onCreate() của Hoạt động B, onStart(), và các phương pháp onResume() thực thi theo trình tự. (Hoạt động B lúc này có tiêu điểm của người dùng.)
  3. Sau đó, nếu Hoạt động A không còn hiển thị trên màn hình, phương pháp onStop() của nó sẽ thực thi.

Trình tự gọi lại vòng đời có thể dự đoán này cho phép bạn quản lý chuyển tiếp thông tin từ hoạt động này sang hoạt động khác. Ví dụ, nếu bạn phải ghi vào một cơ sở dữ liệu khi hoạt động thứ nhất dừng sao cho hoạt động theo sau có thể đọc nó, khi đó bạn nên ghi vào cơ sở dữ liệu trong khi onPause() thay vì trong khi onStop().