[Android] Quản lý Activity, Fragment bằng Task và back stack trong android
Một ứng dụng android thường sẽ bao gồm nhiều activity. Mỗi activity nên được thiết kế xung quanh một kiểu hành động cụ thể mà người dùng có thể thực hiện và bắt đầu các activity khác.
Một ứng dụng android
thường sẽ bao gồm nhiều activity
. Mỗi activity
nên được thiết kế xung quanh một kiểu hành động cụ thể mà người dùng có thể thực hiện và bắt đầu các activity
khác.
Ví dụ: một ứng dụng email có thể có một activity
để hiển thị một danh sách các thư mới. Khi người dùng chọn một thư, một activity
mới sẽ mở ra để xem thư đó. Từ một Activity
của ứng dụng này thậm chí có thể bắt đầu một activity
của một ứng dụng khác.
Ví dụ: ứng dụng của bạn có chức năng gởi email, thay vì tự viết chứng năng này, bạn có thể gọi ứng dụng email, lập tức ứng dụng email sẽ mở màn hình soạn thảo, và sau khi gởi email, activity
của bạn lại tiếp tục và dường như email activity
giống như 1 phần trong ứng dụng của bạn => mặc dù activity
có thể đến từ các ứng dụng khác nhau, nhưng rõ ràng android
đã giúp duy trì trải nghiệm người dùng một cách mượt mà, và cụ thể ở đây android
đã làm việc này bằng cách giữ cả 2 activity
trong cùng một tác vụ (task
). Bài viết này sẽ bàn xoay quanh chủ đề Task và Back stack trong android.
1. Task và Back Stack
Task
là tập hợp gồm nhiềuactivity
mà người dùng tương tác với ứng dụng khi thực hiện một công việc nhất định. Cácactivity
được sắp xếp trong mộtstack
(được gọi làBack stack
), theo thứ tự mở của mỗiactivity
.
Điều gì xảy ra khi bạn ấn vào app icon
? Khi đó hệ thống sẽ tìm kiếm trong những task
đã tồn tại trước đó, nếu có task
nào của ứng dụng đã tồn tại, ứng dụng sẽ được tiếp tục (resume
, hay là đưa ra foreground
). Ngược lại, nếu ứng dụng chưa đc sử dụng gần đây, một task
mới sẽ được tạo cùng với "main" activity
của ứng dụng như là root
(gốc) của Back stack
.
Default behavior for Activity and Task
Khi activity
hiện tại bắt đầu một activity
khác bằng cách gọi startActivity()
, hệ thống sẽ mặc định push
activity
mới lên trên cùng (top
) của Back stack
và giữ focus
vào activity này trong foreground
. Activity
trước đó vẫn sẽ nằm trong stack
nhưng sẽ dừng lại (ở trạng thái stopped
). Khi một activity stop
, hệ thống sẽ giữ lại trạng thái UI (giao diện) hiện tại của người dùng.
Khi người dùng ấn nút Back
, activity
hiện tại (trên cùng) sẽ được pop
ra khỏi Back stack
và bị hủy (ở trạng thái destroyed
), đồng thời activity
trước đó sẽ được tiếp tục (resume
) với trạng thái UI được lưu trước đó. Việc này sẽ lặp lại cho đến khi activity
cuối cùng được pop
ra khỏi Back stack
, lúc này ứng dụng sẽ thoát và trở về màn hình laucher
của device
và lúc này task
của ứng dụng đó sẽ không còn tồn tại.
Các activity
trong Back stack
sẽ không bao giờ được sắp xếp lại, mà chỉ được push
hay pop
từ stack
theo đặc trưng của một stack
LIFO (LAST IN FIRST OUT).
Một Task
là một đơn vị kết dính, nó có thể chuyển xuống background
khi user bắt đầu một task
mới hoặc quay về màn hình Home
bằng cách ấn Home button
. Khi ở trong background
tất cả các activity
trong task
sẽ ở trạng thái stopped
(lúc này gọi là Background task
), nhưng Back stack
vẫn không bị ảnh hưởng. Task
chỉ đơn giản mất đi focus
khi một task
khác thay thế.
Note: Nhiều
task
có thể được lưu giữ cùng lúc trongbackground
. Tuy nhiên, nếu user chạy quá nhiềubackground task
tại cùng thời điểm, hệ thống có thể hủy cácbackground activity
để khôi phục bộ nhớ, lúc này trạng thái củaactivity
bị hủy sẽ mất đi.
Các hành vi mặc định đối với activity
và task
:
- Khi
activity A
bắt đầuActivity B
,Activity A
sẽ bị dừng, nhưng hệ thống sẽ giữ lại trạng thái của nó (VD: vị trí scroll, EditText đang được nhập...), Nếu user ấnBack
khi ởB
,Activity A
sẽ tiếp tục với trạng thái được khôi phục. - Khi user rời khỏi
task
bằng cách ấnHome button
(hoặc mởtask
mới),task
hiên tại sẽ được đưa xuốngbackground
. Hệ thống sẽ giữ nguyên trạng thái của cácactivity
trongtask
, nếu sau đó user tiếp tục ứng dụng,task
sẽ được đưa lênforeground
và tiếp tụcactivity
trên cùng của nó. - Khi user ấn
Back button
,activity
trên cùng củaBack stack
sẽ bị hủy vàpop
ra khỏiBack stack
. Lúc nàyactivity
trước đó sẽ được tiếp tục. Khiactivity
bị hủy, hệ thống sẽ không giữ trạng thái của nó. Activity
có thể được khởi tạo nhiều lần, thậm chí từ cáctask
khác.
Back stack and Fragments
Back stack
không chỉ có tác dụng với activity
mà còn có tác dụng với các fragment
. Khi bạn cung cấp 1 FragmentTransaction
để add, replace, remove
một fragment
từ UI, bạn có thể dùng addToBackStack()
để thêm FragmentTransaction
đó vào trong Back stack
.
Khi ấn Back button
, FragmentTransaction
sẽ được đảo ngược hành động trước đó
- added fragment -> removed
- replaced fragment -> restored
- removed fragment -> added. Mỗi
transaction
thêm vào trongBack stack
sẽ đảo ngược đến khiactivity
chứa chúng bị xóa khỏiBack stack
.
2. Custom Task and Back Stack Behavior
Có những hành vi mặc định đối với activity
và task
, tuy nhiên android
vẫn cung cấp cho chúng ta các cách khác nhau để thay đổi chúng khi cần thiết.
Many same activity in task
Vì các activity
trong Back stack
theo mặc định sẽ không bao giờ được sắp xếp lại, nên sẽ có trường hợp một activity
có thể được tạo nhiều lần nếu nó được khởi tạo từ nhiều lời gọi startActivity()
khác nhau. Thay vì tạo một task
với quá nhiều activity
giống nhau, ta có thể set launchMode = singleTop
trong AndroidManifest
hoặc thêm cờ (flag
) Intent.FLAG_ACTIVITY_SINGLE_TOP
trong Intent
được gọi , khi đó nếu một instance
của activity
đã tồn tại ở trên cùng (top) của task
hiện tại, thay vì tạo một thực thể mới của activity
đó, hệ thống sẽ chỉ đến activity
đó thông qua hàm onNewIntent()
của nó với Intent
mới chứa các extras data
mới nhất.
Ví dụ: Task hiện tại đang là A-B-C-D, D đang nằm ở đỉnh của task. Lời gọi startActivity D, nếu D được mở với standard
launchMode thì một activity
D mới tạo ra, và task sẽ là A-B-C-D-D. Với singleTop
launchMode thì không có activity mới nào được tạo ra vì D đang ở trên cùng của task hiện tại, vì vậy activity
D hiện tại sẽ gọi onNewIntent()
, task vẫn là A-B-C-D. Trường hợp khác nếu gọi A, B, hoặc C thì activity
tương ứng sẽ tạo ra và thêm vào đỉnh Back stack
ngay cả khi được mở bằng singleTop
launchMode. Tham khảo thêm các launchMode
và cách quản lý task
tại đây
Back stack and Notification
Sẽ có trường hợp khi thao tác với notification
, bạn mong muốn mở ra một activity
không phải là activtiy
chính (main
, laucher
), mà là một activity
nằm sâu trong ứng dụng (phải cần qua vài thao tác mới mở được màn hình này). VD: bạn muốn mở DetailActivity
SplashActivtiy -> ListActivity -> DetailActivtiy
Tuy nhiên, vấn đề nảy sinh là khi ấn Back button
, bạn không muốn thoát ứng dụng ngay mà sẽ muốn ứng dụng chuyển đến màn hình cha (hay trước đó) tương ứng như lúc sử dụng bình thường. Điều này xảy ra vì khi click vào notification
, một task
mới sẽ tạo ra với duy nhất activity
được chỉ định trong Back stack
. Vì vậy, nếu không có task
nào khác trong background
, khi ấn Back button
, app sẽ thoát. Giải pháp là sử dụng TaskStackBuilder
: một class đặc biệt để xử lý trường hợp này
// Construct the Intent you want to end up at
Intent detailActivity = new Intent(this, DetailActivity.this);
// Construct the PendingIntent for your Notification
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// This uses android:parentActivityName and
// android.support.PARENT_ACTIVITY meta-data by default
stackBuilder.addNextIntentWithParentStack(detailActivity);
PendingIntent pendingIntent = stackBuilder
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
và trong AndroidManifest
, chỉ rõ activity
cha của DetailActivity
<activity
android:name=".DetailActivity"
android:label="@string/title_activity_order_detail"
android:launchMode="singleTask"
android:parentActivityName=".ListActivity"/>
Note:
addNextIntentWithParentStack()
là một cách ngắn gọn để điều chỉnh choacitivity
được mở ra từnotification
hoạt động theo cách bạn muốn: mởParent Activity
khi nhấnBack button
.android:parentActivityName="PARENT_ACTIVITY"
để chỉ raactivity
cha cụ thể (Xem thêm tại đây)
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!