Chào mừng các bạn đã đến với bài học Android thứ 33, bài học về Fragment (phần tiếp theo). Đây là bài học trong chuỗi bài viết về lập trình ứng dụng Android bằng Java của Yellow Code Books.
Bẵn một khoảng thời gian dài mình không ra bài viết nào cho blog cả. Cũng vì bận khá nhiều chuyện. Mình cũng khá áy náy.
Nhưng dù sao bài viết kế tiếp cho Fragment cũng đã hoàn thành. Hi vọng các bài tiếp theo cũng sẽ được ra lò suôn sẻ. 🙂
Cho đến bài học này, thì mình tin chắc rằng bạn đã biết rõ cách làm việc với Fragment rồi. Tuy nhiên, cũng giống như với Activity, biết sử dụng không có nghĩa là bạn đã hiểu rõ về nó. Và cũng như với các bài học về Activity, khi bạn đã biết cách sử dụng, bạn sẽ tìm cách hiểu rõ cách thức mà Activity làm việc với hệ thống, cụ thể là các kiến thức về Back Stack và vòng đời của Activity. Và vì Fragment cũng như một Activity, thay vì Activity quản lý giao diện của toàn màn hình, thì Fragment lại quản lý giao diện của một phần màn hình bên trong Activity. Do đó, Fragment cũng sẽ có vòng đời của nó, và được hệ thống quản lý như với Activity vậy. Và bài học hôm nay chúng ta sẽ tìm hiểu kỹ kiến thức về Back Stack và vòng đời của Fragment này.
Nếu như với Activity, mình dành cả hai bài mới nói được Back Stack và vòng đời của nó. Thì với Fragment mình nói ngắn gọn và gộp lại thành một bài duy nhất hôm nay.
Fragment Và Back Stack
Chắc bạn còn nhớ, ở bài học số 27, bạn đã biết Back Stack như là một ngăn chứa, nó dùng để chứa các Activity. Và bạn cũng đã biết cơ chế hoạt động của ngăn chứa này, khi Activity được để vào và lấy ra như thế nào. Nếu bạn muốn biết lại chi tiết các vấn đề về Back Stack này, thì hãy đọc lại bài học cũ nhé.
Vậy với việc lĩnh hội thêm kiến thức về Fragment, và với việc biết rằng Fragment cũng được quản lý bởi Back Stack, thì liệu chúng ta có cần phải biết đến một Back Stack nào khác ngoài Back Stack của Activity mà chúng ta đã làm quen hay không? Câu trả lời là Không cần, hệ thống vẫn dùng Back Stack của Activity để quản lý luôn Fragment. Vì cơ bản thì các Fragment sẽ thuộc về Activity mà.
Chúng ta hãy xem với việc xây dựng một cơ chế quản lý Fragment như ở phần này của bài trước đã làm, thì Back Stack của chúng ta sẽ hoạt động như thế nào ở bài học hôm nay nhé.
Ban đầu, từ màn hình chính, TourNote được chạy, Task của TourNote sẽ được tạo, trong Task có một Back Stack chứa màn hình đầu tiên, chính là MainActivity. Bạn đã biết, khi này FirstFragment đã được add vào MainActivity. Bằng chứng là hai Button Item 1 và Item 2 của Fragment này đang được nhìn thấy đó. Cũng chính vì vậy mà MainActivity và FirstFragment đều nằm trên cùng Back Stack, và đều được người dùng nhìn thấy và tương tác.
Nếu lúc này đây người dùng nhấn nút Back từ hệ thống, dĩ nhiên là cả MainActivity và FirstFragment sẽ được lấy ra khỏi Back Stack, và xem như ứng dụng sẽ kết thúc. Vì bạn nên nhớ, FirstFragment hoàn toàn không được thêm vào MainActivity thông qua phương thức addToBackStack(), nên nó không chiếm một ngăn cụ thể trong Back Stack, nó đang bị “dính” với MainActivity ở cùng một ngăn.
Nhưng chúng ta không nhấn Back. Giả sử ở bước này bạn nhấn vào Button Item 1. Khi đó, như code của bài học trước, SecondFragment sẽ thay thế cho FirstFragment. Việc thay thế này có gọi đến addToBackStack(). Và khi đó, FirstFragment mới chính thức được đưa vào quản lý bởi Back Stack, còn SecondFragment sẽ hiển thị ở trên cùng. Mình minh họa Back Stack sẽ trông như thế này.
Nhưng, nếu bạn thắc mắc rằng nếu như không có lời gọi addToBackStack() ở giai đoạn này thì sao? Thì tình huống sẽ bị thay đổi, khi này cái chỗ đang chứa FirstFragment ở Back Stack sẽ bị thay bằng SecondFragment luôn, chứ không có tạo hai ô như hình trên đâu. Bạn có thể thử bằng cách sửa code, nhưng bài học này mình không sửa chữa gì cả, chúng ta cứ theo y kịch bản của code cũ mà thôi.
Tiếo theo, với Back Stack như trên kia, nếu người dùng nhấn Back. Thì cái nằm trên cùng của Back Stack lúc bấy giờ là SecondFragment. Sẽ bị lấy ra trước. MainActivity vẫn còn “sống”, với FirstFragment có sẵn từ trước đó. Nên mọi thứ sẽ trở về như ban đầu.
FirstFragment lúc bấy giờ trở về là giao diện “dính” vào MainActivity như ban đầu, nên khi này nếu người dùng tiếp tục nhấn nút Back. Cả MainActivity và FirstFragment đều bị “xóa sổ”. Như hình minh họa tiếp theo.
Nếu bạn thử nghiệm addToBackStack() ở các dòng code khi gắn FirstFragment vào MainActivity, thì bạn có thể thấy, khi này FirstFragment sẽ là một ngăn trong Back Stack, và nút Back của người dùng như trên đây sẽ vẫn chưa hủy được MainActivity mà chỉ mới gỡ FirstFragment ra khỏi Back Stack mà thôi, bạn thử nhé.
Vòng Đời Fragment
Chúng ta sẽ không nói dông dài về khái niệm vòng đời Fragment nữa, nó hoàn toàn giống với khái niệm vòng đời Activity. Chúng ta đi vào chi tiết sơ đồ của vòng đời Fragment.
Sơ Đồ Minh Họa Vòng Đời Fragment
Bạn có thể nhận thấy vòng đời Fragment cũng khá giống với vòng đời Activity mà bạn đã biết. Có khác chăng là nó có nhiều phương thức callback hơn. Chúng ta sẽ từng bước nói rõ về sơ đồ này ở các mục tiếp theo đây.
Mô Tả Sơ Đồ
Sơ đồ bắt đầu khi Fragment được gắn vào Activity. Khi đó các callback onAttach(), onCreate(), onCreateView(), onActivityCreated(), onStart() và onResume() lần lượt được gọi.
Sau khi các callback trên được gọi, thì Fragment lúc bấy giờ mới chính thức được xem là đang chạy.
Sau đó, nếu người dùng nhấn nút Back, hay có bất kỳ thao tác gỡ/thay thế (remove/replace) Fragment ra khỏi Activity nào, thì các callback onPause(), onStop(), onDestroyView(), onDestroy() và onDetach() sẽ được gọi.
Nhưng có một cái hay là, nếu Fragment được đưa vào Back Stack kèm với lệnh gỡ/thay thế, thì onDestroy() và onDetach() sẽ chưa được gọi ngay. Để khi rơi vào trường hợp sau đó khi Fragment này được hiển thị lại trong Back Stack, thì onCreateView() sẽ được gọi lại.
Chỉ vậy thôi, chúng ta sẽ xem xét kỹ hơn về từng trạng thái chính của sơ đồ vòng đởi này.
Các Trạng Thái Chính Trong Vòng Đời Fragment
Bạn sẽ thấy các trạng thái chính này cũng không khác gì với Activity cả, nhưng hãy xem các tình huống cụ thể liên quan với nhau giữa Fragment và Activity như thế nào nhé.
Hoạt Động (Active Hay Resume)
Khi Fragment được gắn vào Activity, được nhìn thấy và có thể tương tác được.
Tạm Dừng (Pause)
Cũng khá giống với trạng thái tạm dừng của Activity. Tức là nếu Activity có chứa Fragment này bị che lấp bởi Activity khác (nhưng không bị che hoàn toàn, người dùng vẫn nhìn thấy được Activity bị che lấp, chỉ là không tương tác được), thì cả Activity và Fragment đó đều vào trạng thái tạm dừng.
Dừng (Stop)
Cũng giống với Activity, Fragment bị dừng khi bị thành phần nào đó che khuất hoàn toàn. Hay bị gỡ ra khỏi Activity.
Dừng chưa phải là chấm hết cho đời sống của Fragment. Cụ thể là các trạng thái của nó vẫn còn được lưu trữ, để phòng trường hợp Fragment này được trở lại hiển thị cho người dùng.
Chết (Dead)
Nếu Fragment bị gỡ ra khỏi Activity, nhưng không được đưa vào Back Stack trước đó, thì nó sẽ kết thúc vòng đời. Hoặc khi Activity chứa Fragment này bị gỡ khỏi Back Stack, Fragment cũng sẽ chết theo.
Làm Quen Với Các Callback
Sau đây là ý nghĩa của từng callback
onAttach()
Callback này được gọi khá sớm, ngay khi Activity chứa nó được kích hoạt. Hoặc ngay khi được gắn vào Activity.
Callback này được gọi một lần duy nhất trong vòng đời Fragment. Và ở giai đoạn này Fragment đã “nhận biết” được Activity chứa nó rồi, nên bạn có thể tận dụng để kiểm tra sớm một số điều kiện nào đó như các dòng code ở FirstFragment chúng ta đã từng làm.
onCreate()
Callback này được gọi khi Fragment bắt đầu khởi tạo từ các dữ liệu đầu vào.
Khác với onCreate() của Activity, rằng bạn có thể tạo giao diện cho màn hình ở callback này, thì với Fragment chúng ta còn phải đợi qua callback tiếp theo mới có thể tạo giao diện được.
Callback này cũng được gọi một lần trong đời sống Fragment. Nên thường tận dụng để lấy dữ liệu từ bên ngoài truyền vào như ở SecondFragment chúng ta có làm quen.
onCreateView()
Khi Fragment bắt đầu vẽ UI lên màn hình, callback này được gọi. Nên chúng ta sẽ tận dụng callback này cho các thiết lập về giao diện.
Bạn thấy rằng, theo như sơ đồ trên, thì callback này sẽ được gọi lại khi mà Fragment được gỡ ra khỏi Activity nhưng được đưa vào Back Stack, và được gọi lại hiển thị sau đó.
Khi kết thúc callback này, hãy nhớ return một View như những gì bạn đã thử nghiệm với FirstFragment và SecondFragment. Lưu ý là chúng ta hoàn toàn có thể return null nếu Fragment không có UI.
onActivityCreated()
Callback này được gọi ngay sau khi onCreateView() được gọi. Nó báo hiệu trạng thái Activity chứa nó được khởi tạo hoàn toàn. Tuy ít được sử dụng hơn các callback khác, nhưng bạn cũng có thể tận dụng nó để thay đổi giao diện hay các tương tác với Activity chứa Fragment này thoải mái được rồi.
onStart()
Khi Fragment bắt đầu được nhìn thấy bởi người dùng và chuẩn bị nhận tương tác.
onResume()
Người dùng hoàn toàn nhìn thấy và tương tác được với Fragment.
onPause()
Callback này như một dấu hiệu cho thấy rằng người dùng đang rời khỏi Fragment hiện tại. Mặc dù không phải lúc nào onPause() được gọi là người dùng sẽ bái bai Fragment này. Nhưng bạn nên sao lưu các dữ liệu cần thiết của Fragment ở callback này, nhỡ đâu người dùng thực sự rời đi không quay lại thì sao.
onStop()
Fragment chính thức không còn được nhìn thấy nữa.
onDestroyView()
Chắc chắn là đối tượng View sẽ bị hủy ở callback này. Và do đó các khởi tạo view của bạn ở onCreateView() sẽ nhanh chóng không còn nữa.
Nếu như Fragment được đưa vào Back Stack, thì khi được lấy ra lại sau đó, callback onCreateView() sẽ được gọi lại.
onDestroy()
Fragment đã sắp “chết”. Nhưng khác với Activity, khi onDestroy() của Activity được gọi thì xem như Activity đã đến “cuối đời”. Còn với Fragment, callback này chỉ như một lời “nhắc nhở” về vận mệnh của Fragment mà thôi.
onDetach()
Callback này gọi đến báo hiệu Fragment sẽ được gỡ khỏi Activity đang chứa nó. Kết thúc vòng đời của Fragment.
Chúng ta vừa xem qua một kiến thức về quản lý Fragment, bởi hệ thống. Thông qua đây bạn được hiểu rõ hơn về các callback và cách sử dụng chúng trong các logic của Fragment sau này.
Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao bên dưới mỗi bài nếu thấy thích.
– Comment bên dưới mỗi bài nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
Bài Kế Tiếp
Bài sau chúng ta sẽ nói tiếp một kiến thức nhỏ nữa về Fragment, đó là các thể loại Fragment bên trong Android mà bạn có thể sử dụng. Và sẽ cùng xây dựng Fragment cụ thể cho TourNote.
Anh sớm cho ra những bài viết mới trong chuỗi bài này nhé. Em cảm ơn!
Mình sẽ cố gắng, hehe
Bạn nhớ đề cập đến khái niệm Child Fragment nữa nhé!
Ồ đúng vậy, mình bị sót thông tin này, cảm ơn bạn xhuy8x nhiều, haha, để mình tìm ví dụ bổ sung vào bài viết này hoặc có bài viết nào riêng biệt.
Cảm ơn bạn vì những bài viết này rất hay và bổ ích. Mong bạn có thể viết nhiều hơn để mình có thể học hỏi từ bạn.
e đọc và thực hành 1 lèo tới bài này, bài nào cũng vote 5*, tut của bác dễ hiểu và hay lắm, b sớm ra các bài khác nhé, cảm ơn b nhiều!
Quá tốt! Ai cũng vote 5* như bạn thì mình sẽ vui lắm. Cảm ơn bạn!
Chưa đọc ở đâu mà bài viết hay và chất lượng như của anh.
Em thích cách viết của anh. Hay mà dễ hiểu :))
Chúc anh sức khỏe và luôn giữ vững được lửa 😉
Cảm ơn bạn minhphong306 nhiều nhé!
Cảm ơn anh nhiều, các bài viết rất hay.
Bài rất hay ạ! Cảm ơn anh nhiều.
Cho em hỏi là ví dụ một activity host 2 fragment, kiểu như activity A -> fragment B -> fragment C (lần lượt như vậy ạ và fragment B vs C chiếm toàn màn hình luôn ạ). Cho em hỏi là nếu như ở fragment C bình hủy ứng dụng, thì onDetach callback của fragment B có gọi ở lúc này không ạ? Em cảm ơn nhiều ạ