12/08/2018, 14:58

Task and 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. 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 ...

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.

Task và Back Stack

Definition

Task là tập hợp gồm nhiều activity 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ác activity được sắp xếp trong một stack (được gọi là Back stack), theo thứ tự mở của mỗi activity.

Đ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 trong background. Tuy nhiên, nếu user chạy quá nhiều background task tại cùng thời điểm, hệ thống có thể hủy các background activity để khôi phục bộ nhớ, lúc này trạng thái của activity bị hủy sẽ mất đi.

Các hành vi mặc định đối với activity và task:

  1. Khi activity A bắt đầu Activity 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 ấn Back khi ở B, Activity A sẽ tiếp tục với trạng thái được khôi phục.
  2. Khi user rời khỏi task bằng cách ấn Home button (hoặc mở task mới), task hiên tại sẽ được đưa xuống background. Hệ thống sẽ giữ nguyên trạng thái của các activity trong task, nếu sau đó user tiếp tục ứng dụng, task sẽ được đưa lên foreground và tiếp tục activity trên cùng của nó.
  3. Khi user ấn Back button, activity trên cùng của Back stack sẽ bị hủy và pop ra khỏi Back stack. Lúc này activity trước đó sẽ được tiếp tục. Khi activity bị hủy, hệ thống sẽ không giữ trạng thái của nó.
  4. Activity có thể được khởi tạo nhiều lần, thậm chí từ các task 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 trong Back stack sẽ đảo ngược đến khi activity chứa chúng bị xóa khỏi Back stack.

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 cho acitivity được mở ra từ notification hoạt động theo cách bạn muốn: mở Parent Activity khi nhấn Back button. android:parentActivityName="PARENT_ACTIVITY" để chỉ ra activity cha cụ thể (Xem thêm tại đây)

Tham khảo:

https://medium.com/google-developers/tasks-and-the-back-stack-dbb7c3b0f6d4

https://developer.android.com/guide/components/activities/tasks-and-back-stack.html

https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=MvIlVsXxXmY

0