12/08/2018, 13:19

Hiểu về Activity''s launchMode trong Android

Activity là một khái niệm cơ bản nhất, cốt lõi nhất (most brilliant) trong Android, nó là một một kiến trúc thiết kế tốt về quản lý bộ nhớ cho phép hoạt động đa nhiệm một cách hoàn hảo trên hệ điều hành di động phổ biến nhất hiện nay - Android. Dù sao, Activity không phải chỉ là để được đưa ra ...

Activity là một khái niệm cơ bản nhất, cốt lõi nhất (most brilliant) trong Android, nó là một một kiến trúc thiết kế tốt về quản lý bộ nhớ cho phép hoạt động đa nhiệm một cách hoàn hảo trên hệ điều hành di động phổ biến nhất hiện nay - Android.

Dù sao, Activity không phải chỉ là để được đưa ra trên màn hình. Cách nó được đưa ra như thế nào cũng là một vấn đề cần được quan tâm. Có rất nhiều vấn đề trong chủ đề này, và một trong những vấn đề khá quan trọng đó là launchMode của activity.

Bởi vì mỗi Activity được thiết kế để thực hiện một công việc, việc làm với mục đích khác nhau. Một số được thiết kế như một Intent gửi email, trong khi một số được thiết kế như một singleton như là email inbox Acitivity.

Đó là lý do tại sao cần xác định một Activity cần được khởi tạo mới, hay là được sử dụng từ cái có sẵn, đây là vấn đề có thể dẫn tới bad UX hoặc malfunctional. Và launchMode chính là một cách đơn giản để giải quyết vấn đề này.

Khai báo launchMode Cách cơ bản nhất là sử dụng thẻ <activity> và khai báo nó trong file AndroidManifest.xml:

<activity
    android:name=".Activity"
    android:label="ActivityLaunchMode"
    android:launchMode="singleTask"/>

Và chúng ta có 4 loại launchMode. Sau đây ta sẽ đi tìm hiểu từng loại launchMode.

1. Standard

Đây là launchMode mặc định. Một activity mới sẽ luôn được khởi tạo mới cho tác vụ riêng với mỗi Intent gửi đi. Tức là activity đó có thể được khởi tạo mới nhiều lần, mỗi Intent có thể thuộc về các task khác nhau, và một task có thể có nhiều Intent.

ví dụ: nếu có 10 Intent được gửi để soạn thảo email, thì sẽ có 10 activity được khởi tạo với mỗi Intent riêng biệt, như vậy là có thể sẽ không giới hạn số activity được khwoir tạo trên một thiết bị.

Trước version Lollipop

Acitivity sẽ được khởi tạo và đặt ngay trên đỉnh của stack trong cùng một task

1.jpg

Hình dưới sẽ mô tả cụ thể hoạt động của standard Activity, nó sẽ được đặt trong cùng một task mặc dù đó là những activity từ các ứng dụng khác nhau.

2.jpg

Và khi ta nhìn vào Task Manager

3.jpg

Nếu ta chuyển sang một ứng dụng khác và quay lại Gallery, ta vẫn thấy nó ở top của của Gallery task. Như vậy nếu ta cần làm bất cứ điều gì với Gallery thì ta phải finish Activity ở top của task.

Từ version Lollipop

Nếu các Activity đều từ cùng một ứng dụng, thì nó vẫn hoạt động như những version trước đó, tức là activity vẫn được stack vào top của task.

4.jpg

Những đối với các Intent được gửi từ những ứng dụng khác, một task mới sẽ được khởi tạo và activity mới được khởi tạo sẽ được đặt làm root Acitivity.

5.jpg

Nhìn vào Task Manager

6.jpg

Như ta thấy, Task Manager trong Lollipop được sử đổi tốt hơn và trực quan hơn. Ta chỉ có thể chuyển về Gallery khi mà nó được đặt ở task khác.

Vậy nếu bạn cần một Activity có thể thực hiện một công việc riêng biệt để phục mục một Intent riêng biệt thì hãy nghĩ đến Standard mode.

2. SingleTop

SingleTop hoạt động gần giống với Standard launchMode. Một Activity có thể được khởi tạo nhiều lần, mỗi Intent có thể thuộc về nhiều task khác nhau, và một task có thể chứa nhiều Intent (nhưng chỉ khi activity ở top của back stack không phải là một thể hiện của activity này). Điểm khác biệt là nếu một thể hiện của activity đã được tồn tại ở top của task hiện tại thì sẽ không có một activity nào mới được tạo ra, thay vì đó Intent sẽ được gửi tới thể hiện của activity hiện tại thông qua phương thức onNewIntent().

7.jpg

Trong singleTop mode, ta phải quản lý các Intent đến trong cả onCreate() và onNewIntent() để kiểm soát tất cả trường hợp khi khởi tạo, hay thực hiện một thể hiện mới của Activity.

ví dụ: Nếu ta luôn khởi tạo mới một SearchActivity để phục vụ những yêu cầu tìm kiếm mới, thì 10 Activity sẽ được tao cho 10 searching. Như vậy để quay lại root Activity ta phải back lại 10 lần tương ứng với 10 searching. Thay vào đó, nếu SearchActivity ở top của task, ta có thể send một Intent đến thể hiện của activity hiện tại và update kết quả tìm kiếm. Và giờ ta chỉ có một SearchActivity ở top của stack và chỉ cần ấn một lần nút back để quay về activity trước đó. Điều này tạo nên một trải nghiệm tốt hơn.

SingleTop hoạt động trên cùng một task. Nếu một Intent được gửi đến một Activity (singleTop) đang tồn tại ở top của bất kỳ task khác, thì một Activity sẽ được tạo mới giống như standard launchMode(trước L: activity được đặt ở top của task gọi; từ L: một task mới sẽ được tạo ra).

3. SingleTask

Đây là mode rất khác so với standard và singleTop. Một singleTask Activity được phép chỉ có duy nhất một thể hiện trên hệ thống (a.k.a Singleton). Nếu một thể hiện của Activity đã tồn tại trên hệ thống, tất cả Task chứa thể hiện đó sẽ được di chuyển lên top, đồng thời Intent sẽ được thực hiện thông qua phương thức onNewIntent(). Activity sẽ được tạo và đặt ở vị trí thích hợp.

Làm việc trên cùng một ứng dụng

Nếu nó không phải là một thể hiện của singleTask Activity đã có trên hệ thống, thì nó sẽ được tạo mới và đặt vào top của stack trong cùng một Task.

8.jpg

Những nếu một singleTask Activity đã tồn tại, thì tất cả activity được đặt trên activity này đều được tự động destroy một cách phù hợp (lifecycle trigged). Trong khi đó, một Intent sẽ được gửi đến singleTask Activity thông qua phương thức onNewIntent().

9.jpg

Nhưng từ thực nghiệm, nó hoạt động không giống như được mô tả. Một singleTask Activity vẫn stack up ở top của Task'Activity stack, ta có thể sử dụng command: dumpsys activity để thấy điều đó:

Task id #239
  TaskRecord{428efe30 #239 A=com.thecheesefactory.lab.launchmode U=0 sz=2}
  Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.thecheesefactory.lab.launchmode/.StandardActivity }
    Hist #1: ActivityRecord{429a88d0 u0 com.thecheesefactory.lab.launchmode/.SingleTaskActivity t239}
      Intent { cmp=com.thecheesefactory.lab.launchmode/.SingleTaskActivity }
      ProcessRecord{42243130 18965:com.thecheesefactory.lab.launchmode/u0a123}
    Hist #0: ActivityRecord{425fec98 u0 com.thecheesefactory.lab.launchmode/.StandardActivity t239}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.thecheesefactory.lab.launchmode/.StandardActivity }
      ProcessRecord{42243130 18965:com.thecheesefactory.lab.launchmode/u0a123}

Để một singleTask Activity hoạt động như được mô tả: tạo mới Task và đặt Activity vào root thì ta cần phải khai báo thuộc tính taskAffinity

<activity
    android:name=".SingleTaskActivity"
    android:label="singleTask launchMode"
    android:launchMode="singleTask"
    android:taskAffinity="SingleTaskActivity">

10.jpg

11.jpg

Tùy thuộc vào hình vi hoạt động của Activity mà bạn nên xem xét có nên khai báo thuộc tính taskAffinity cho Activity hay không.

Kết hợp với các ứng dụng khác

Lần đầu một Intent được gửi từ một ứng dụng khác và không có bất kỳ thể hiện của Activity đó được tạo trên hệ thống, một new Task sẽ được tạo mới với Activity được tao mới nhất, và đặt nó vào root Activity.

12.jpg

13.jpg

Trừ khi đó là một Task của ứng dụng gọi đến singleTask Activity có sẵn, thì Activity mới nhất sẽ được đặt ở top.

14.jpg

Trong trường hợp đò là thể hiện của activity đã có sẵn tại bất kỳ Task nào, tất cả Task sẽ được di chuyển lên top và bất kỳ single Activity được đặt ở trên singleTask Activity sẽ bị destroy với lifecycle. Nếu nút back được ấn, user sẽ phải đi qua tất cả activity trong stack trước khi quay lại caller Task.

15.jpg

ví dụ: bất kỳ Entry Point Activity cho email client's Inbox hay Social Network's Timeline đều được thiết kế để không có quá mọt thể hiện, nên singleTask sẽ có thể thực hiện một cách rất tốt điều này. Đồng thời cũng cần sử dụng một cách khôn quan vì Activity có thể bị destroy mà user không công nhận.

4. SingleInstance

SingleInstance rất giống với singleTask mode. Chỉ có một trường hợp duy nhất của Activity được tồn tại trên hệ thống. Điểm khác biệt là Task chứa Activity chỉ chứa duy nhất một Activity, một singleInstance. Nếu một Activity khác được gọi từ Activity loại này, một Task mới sẽ được tự động khởi tạo để đặt Activity mới. Tương tự như vậy, nếu một singleInstance Activity được gọi, một Task mới sẽ được khởi tạo để đặt Activity.

Theo những thông tin được cung cấp bởi dumpsys, có thể thấy 2 Task trong hệ thống, nhưng chỉ có một Task được xuất hiện trong Task Manager phụ thuộc vào cái nào được di chuyển lên top lần cuối. Vậy nên, mặc dù có task vẫn hoạt động ở background nhưng chúng ta vẫn không thể di chuyển nó lên foreground.

16.jpg

nhìn trong Task Manager

17.jpg

Vì task này chỉ có thể có một Activity, nên ta không thể switch back lại Task#1 được nữa. Chỉ có một cách đó là chạy lại dứng dụng từ launcher nhưng khi đó singleInstance sẽ bị ẩn ở background. Tuy nhiên ta có thể khai báo thuộc tính taskAffinity (như singleTask) để thực hiện việc trên.

18.jpg

Mode này ít được sử dụng. Một số trường hớp được sử dụng như là một Activity cho Launcher hay nếu bạn chắc chắn 100% rằng ứng dụng chỉ cần duy nhất một Activity. Lời khuyên là: Không nên sử dụng chế độ này, trừ khi thật cần thiết.

5. Intent Flags

Bên cạnh cách khai báo trực tiếp trong file AndroidManifest.xml, ta có thể định hành vi thông qua Intent Flags:

Intent intent = new Intent(StandardActivity.this, StandardActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);

Ta có thể tìm hiểu cụ thể hơn các flags tại Intent

Nguồn: http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en

0