Chào mừng các bạn đã đến với bài học Android thứ 27, bài học về việc hệ thống quản lý hiển thị các Activity thông qua Back Stack như thế nào. Đâ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.
Nếu như ở bài hôm trước, chúng ta đã cùng nhau tạo một Activity mới cho TourNote – ContactActivity. Chắc chắn bạn đã cảm thấy thích thú với việc từ một Activity ban đầu, là MainActivity, nhấn vào item menu trên ActionBar một cái, hệ thống sẽ chuyển sang màn hình mới chính là ContactActivity. Rồi nhấn Back button ở ActionBar hay Back ở System Bar sẽ dẫn bạn quay về MainActivity ban đầu. Vậy thì có khi nào bạn thắc mắc rằng các màn hình bên trong một ứng dụng Android sẽ luân phiên hiển thị ra như thế nào? Một màn hình hiển thị ra thì màn hình kia đi về đâu, có bị xóa khỏi hệ thống không? Tại sao nhấn nút Back ở System Bar lại giúp quay về màn hình trước trong khi chúng ta chẳng có code gì chỗ này cả? Vậy nhấn nhiều nút Back thì sẽ đi đến đâu? Vân vân…
Thì bài học hôm nay chúng ta sẽ chỉ đi vào lý thuyết, để trả lời cho các câu hỏi trên kia thông qua việc giải nghĩa khái niệm và cách hoạt động của Back Stack. Mặc dù chỉ là lý thuyết, nhưng những kiến thức của bài hôm nay sẽ làm nền tảng, được ứng dụng nhiều, và sẽ được mình nhắc đến nhiều ở các bài học sau đấy nhé.
Trước khi đi chi tiết vào khái niệm thế nào là Back Stack, chúng ta phải hiểu được phản ứng của hệ thống lên các ứng dụng thông qua việc khảo sát ba nút “thần thánh” trên System Bar của hệ điều hành Android nào.
Back, Home Và Overview
Bất cứ ai sử dụng thiết bị Android cũng đều quen thuộc với ba nút mặc định được đặt ở System Bar phía dưới cùng của màn hình này.
Dù cho hình dáng và vị trí của chúng có thể khác nhau vào từng thời điểm của hệ điều hành, hoặc thiết bị phần cứng. Thì chung quy lại chúng vẫn có ba chức năng chính, đó là Back, Home và Overview. Có lẽ mình cũng không cần giải thích tác dụng của ba nút này, vì chắc chắn bạn đang thao tác với chúng hàng ngày rồi. Chúng ta sẽ chỉ nói đến việc hành xử của hệ thống đối với ứng dụng khi người dùng nhấn vào các nút đặc trưng này mà thôi.
Giờ giả dụ bạn vừa mở nguồn điện thoại lên, tức là chưa có ứng dụng nào được chạy. Ở màn hình chính của thiết bị, bạn tìm đến và nhấn vào một icon app nào đó để mở một ứng dụng lên. Mình giả sử TourNote được chọn để khởi chạy. Ngay lập tức, Activtity được đánh dấu là launcher của TourNote sẽ được thực thi. Activity là launcher chính là Activity có khai báo intent-filter ở Manifest như bài học trước bạn có thực hành qua. Người dùng sẽ nhanh chóng nhìn thấy giao diện của MainActivity. Rồi từ MainActivity, bạn lại chọn menu item của ActionBar để đi đến ContactActivity. Bạn hãy nhìn vào sơ đồ sau, các dấu mũi tên đứt nét cho thấy thao tác kích hoạt Actvitiy, còn khung đứt nét chính là Activity đang được hiển thị.
Tại ContactActivity, nếu bạn nhấn nút Back. Như bạn biết, ContactActivity sẽ “đóng lại” và nhường màn hình lại cho MainActivity. Nếu nhấn tiếp Back, hệ thống sẽ “thu hồi” màn hình và nhường lại cho màn hình chính của hệ thống. Vậy chuyện gì xảy ra với các màn hình khi bạn nhấn nút Back? Bạn có thể tưởng tượng được rằng nếu như có các Activity được mở bên trong một ứng dụng, việc nhấn Back sẽ giúp quay lại Activity đã mở trước đó một cách tự động, đến khi không còn bất kỳ Activity nào khác của ứng dụng đang mở, ứng dụng sẽ bị đóng lại hoàn toàn. Bạn có thể thấy rằng tư duy lập trình cho ứng dụng Android khác hoàn toàn với các ứng dụng trên desktop ở điểm này. Đó là bạn sẽ không cần phải xây dựng bất kỳ nút nào đại loại như “Thoát ứng dụng” để mà đóng ứng dụng một cách máy móc cả, chính hành động nhấn nút Back của người dùng sẽ là cách tốt nhất để nói cho hệ thống “hãy quay lại màn hình trước”, và màn hình trước ngay cả khi bạn khởi chạy TourNote chính là màn hình chính của thiết bị. Hình sau minh họa việc nhấn nút Back ở các đường mũi tên cong đứt nét, còn dấu chéo cho biết hệ thống sẽ đóng các màn hình đó lại bởi thao tác nhấn nút Back này.
Bạn có thể nghĩ rằng một khi nhấn các nút Back để quay trở về màn hình chính, thì ứng dụng sẽ bị “xóa sổ” hoàn toàn khỏi hệ thống? Không. Ứng dụng vẫn sống một lúc nữa để phòng khi người dùng đổi ý muốn quay lại sử dụng, khi đó họ sẽ không phải đợi quá lâu để ứng dụng khởi động lại từ đầu. Bạn có thể tự tìm hiểu thêm việc này vì chúng ta không đủ giấy mực để nói ở bài hôm nay.
Giờ chúng ta giả dụ đang ở ContactActivity, nhưng thay vì nhấn Back, bạn lại nhấn Home, chuyện gì sẽ xảy ra? Ứng dụng sẽ về màn hình chính ngay. Một tình huống khác hoàn toàn với khi nhấn Back. Nhưng đôi khi cũng sẽ dễ bị nhầm lẫn nếu người dùng đang ở Activity đầu tiên của ứng dụng, khi đó việc nhấn Back hay Home cũng sẽ dẫn người dùng về màn hình chính. Khác nhau cơ bản giữa Back và Home là, việc nhấn Back sẽ kêu hệ thống hủy các Activity đi, và có thể dẫn đến hủy luôn ứng dụng nếu không còn Activity nào khác phải hủy, việc quay lại ứng dụng sau đó sẽ luôn dẫn bạn đi lại từ các màn hình đầu tiên. Còn nhấn Home sẽ không làm hủy dứng dụng hay các Activity, chúng được đưa về background và ở đó, và người dùng hoàn toàn có thể quay trở lại ngay Activity mà trước đó đang hiển thị thì bạn nhấn Home.
Vậy thì các hành xử khi nhấn Back hoặc Home này của hệ thống có ảnh hưởng gì đến các ứng dụng khác và cả TourNote của chúng ta? Câu trả lời là có. Chính vì vậy mình mới cố gắng viết phần này, mặc dù biết các bạn đang buồn ngủ vì đọc cái điều mà ai cũng biết là gì rồi. TourNote sẽ bị ảnh hưởng như thế nào thì chúng ta cùng xem qua tổ chức của hệ thống thông qua các hành xử này nhé. Bắt đầu với việc tổ chức Task.
Task Là Gì?
Hiển nhiên bạn đã biết nghĩa tiếng Việt của Task chính là Tác vụ. Vậy Tác vụ là gì? Thực ra Task hay Tác vụ trong một hệ thống Android sẽ cần rất nhiều mô tả. Nhưng bạn có thể hiểu một cách trực quan như ví dụ mở ứng dụng trên đây. Mỗi một ứng dụng được khởi chạy trong Android sẽ được hệ thống xem là một Task.
Khi mà người dùng nhấn vào một icon app nào đó trên màn hình chính của thiết bị. Hoặc từ danh sách các Task khi nhấn vào nút Overview như hình trên đây. Thì việc đầu tiên hệ thống sẽ xem xem Task hiện tại của ứng dụng đó có tồn tại hay không. Nếu chưa có thông tin Task của ứng dụng đó trong hệ thống, hệ thống sẽ tạo mới một Task cho nó. Còn nếu ứng dụng đã có sẵn một Task, có nghĩa là bạn đã chạy ứng dụng trước đó rồi nhưng bạn đã nhấn Home để về màn hình chính, và Task cũ của nó vẫn còn đang “sống”, thì việc của hệ thống là nhìn vào trong Task đang chạy đó để tìm kiếm vài thông tin, trong đó có thông tin về Activity mà Task đó đang hiển thị là gì, để mà tiếp tục hiển thị lại cho bạn. Ồ vậy trong Task có chứa cấu trúc gì mà hay thế?
Trong Task của mỗi ứng dụng sẽ chứa đựng thông tin về Back Stack. Chính Back Stack này sẽ nói cho hệ thống biết nên hiển thị Activity nào của ứng dụng.
Vậy cụ thể về Back Stack là như thế nào và chúng hoạt động ra sao? Chúng ta cùng qua mục tiếp theo.
Back Stack Là Gì?
Bạn cứ tưởng tượng Back Stack là một ngăn đựng đồ. Khi bạn để một món đồ vào ngăn, nó sẽ nằm ở đáy ngăn (như món số 1 trong hình dưới). Món kế tiếp và kế tiếp nữa sẽ đè lên các món ban đầu (món số 2 và 3). Và món đồ được để vào cuối cùng sẽ là món được lấy ra trước nhất, vì nó nằm trên cùng của ngăn đựng đồ (chính là món số 3).
Vậy món đồ mà Back Stack sẽ chứa đựng là gì? Đó không gì khác chính là các Activtiy mà chúng ta đã nói đến. Việc hệ thống quản lý xem một Activity nào được hiển thị, và Activity nào phải nhường chỗ cho Activity khác, đều chỉ dựa vào cái ngăn chứa này thôi đấy.
Quay lại ví dụ trên kia. Giả sử vẫn chưa có Task nào của TourNote được tạo. Thì khi bạn nhấn vào icon app TourNote ở màn hình chính. Hệ thống sẽ tạo ra một Task cho TourNote, trong Task này có một Back Stack, trong Back Stack chứa đựng sẵn một Activity ban đầu được khai báo là launcher, chính là MainActivity. Bạn có thể nhìn vào minh họa Back Stack đang chứa Activity nào như bên phải của hình dưới đây.
Sau đó, với việc gọi phương thức startActivity() từ MainActivity, sẽ dẫn đến việc ContactActivity được kích hoạt như ở bài thực hành trước. Việc kích hoạt này sẽ đặt thêm ContactActivity vào Back Stack. Và hệ thống cũng căn cứ vào Back Stack mà cho MainActivity về background (không bị hủy nhé) vì nó đã không còn nằm trên cùng của ngăn chứa. Còn ContactActivity vì nằm ở trên cùng của Back Stack nên được hiển thị.
Giờ đây, nếu người dùng nhấn nút Back. Hoặc cũng có thể gọi phương thức finish() như ở bài thực hành trước. Dĩ nhiên, dù bằng đường nào, thì ContactActivity cũng sẽ bị lấy ra khỏi Back Stack. Và ContactActivity lúc này mới chính thức bị hủy. Màn hình lúc này cũng sẽ nhường chỗ lại cho Activity trên cùng của Back Stack lúc bấy giờ, chính là MainActivity.
Lúc này, nếu người dùng tiếp tục nhấn Back. MainActivity bị hủy. Và khi đã hủy Activity cuối cùng ra khỏi Back Stack, như mình có nói trên kia, hệ thống sẽ xem như ứng dụng đã thoát.
Vậy với nút Home thì sao? Nếu bạn đang ở bất kỳ Activity nào, chẳng hạn như ở ContactActivity mà nhấn Home. Thì sẽ không có gì bị xóa khỏi Back Stack cả. Tất cả thông tin trong Task được giữ lại và hệ thống chỉ là chuyển tất cả Task của ứng dụng về background để nhường chỗ cho một Task khác, chính là Task của màn hình chính. Sau này khi bạn mở lại ứng dụng, nếu Task không bị xóa (Task có thể sẽ bị xóa nếu ở background quá lâu và chiếm dụng hiệu năng của hệ thống), thì Back Stack sẽ được hệ thống nhìn vào và tái hiện lại Activity đang ở trên cùng của Stack.
Có một lưu ý với Back Stack mà ở ngay bài học hôm nay bạn có thể ráng nhớ. Đó là, chúng ta hoàn toàn có thể đặt vào Back Stack mọi Activity, thậm chí có thể đặt nhiều bản copy của một Activity. Chẳng hạn ở MainActivity bạn thực hiện kích hoạt ContactActivity để đưa Activity này vào Back Stack, nhưng ở ContactActivity, khi bạn muốn trở về MainActivity, thay vì gọi finish() bạn lại startActivity() để kích hoạt lại MainActivity, điều này sẽ tiếp tục đẩy một phiên bản copy nữa của MainActivity vào Back Stack. Thì việc đặt nhiều bản copy của một Activity vào Back Stack như vậy là không nên. Chúng khiến cho thao tác Back của người dùng bị loạn xà ngầu lên, và không tốt cho hiệu năng của hệ thống nữa. Bạn ghi chú điều này để hiểu tại sao lại phải gọi finish() ở ContactActivity, và cho các tình huống cụ thể sau này nữa.
Trên đây là kiến thức thuần lý thuyết về cách mà hệ thống sử dụng Back Stack để điều khiển các Activity của các ứng dụng. Về sau, Back Stack này sẽ còn được nhắc đến nhiều, đặc biệt là khi nói về vòng đời của Activity ở bài kế, và bài học về Fragment ở các bài kế nữa.
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
Chúng ta sẽ xem cụ thể vòng đời của một Activity như thế nào khi em ấy “ngụp lặn” trong Back Stack của hệ thống nhé.
bài viết hay nhưng sao không viết tiếp nữa vậy admin
Bạn chuẩn bị tinh thần nha, bài kế tiếp sẽ có ngay thôi 😀
Nội dung bài viết hay đó.