+3

Tìm hiểu Android launchMode

Activity là một trong những khái niệm truyệt vời nhất trên Android từ kiến trúc thiết kế tốt về quản lý bộ nhớ cho phép đa nhiệm hoạt động hoàn hảo trên hệ điều hành di động phổ biến nhất hành tinh này.

Dù sao thì Activity không chỉ đơn thuần là được khởi chạy trên màn hình. Cách mà chúng được khởi chạy trên màn hình cũng nhận được sự quan tâm. Trong bài viết lần này, chúng ta sẽ nói về launchMode.

Mỗi Activity được tạo ra để hoạt động trong các mục đích khác nhau. Một số thì được thiết kế để hoạt động riêng biệt với mỗi Intent được gửi, ví dụ như Activity soạn Email trong ứng dụng Email của khách hàng. Trong khi đó một số được thiết kế để hoạt động như một singleton, ví dụ như Activity hòm thư đến.

Đó là lý do tại sao cần chỉ định Activity là cần thiết để tạo một cái mới hoặc sử dụng một cái có sẵn, hoặc nó có thể dẫn đến trải nghiệm người dùng không tốt hoặc hoạt động sai chức năng. Đây là cách dễ dàng để thực hiện nó với một số trợ giúp của launchMode được thiết kế đặc biệt cho việc này.

1. Cách chỉ định một launchMode

  • Về cơ bản, chúng ta có thể chỉ định một launchMode trực tiếp như thuộc tính của thẻ <activity> trong file androidManifest.xml, kiểu như này:
    <activity
              android:name = ".MainActivity"
              android:exported = "true"
              android:launchMode = "standard">
    
  • Hiện tại chúng ta có tất cả 5 mode có sẵn, gồm: standard, singleTop, singleTask, singleInstance, singleInstancePerTask.

2. Chế độ standard

  • Đây là chế độ mặc định.

  • Các Activity được đặt thành chế độ này khi được tạo mới sẽ luôn hoạt động riêng biệt với mỗi Intent được gửi. Hãy tưởng tượng, nếu có 10 Intent được gửi để soạn email, thì cần có 10 Activity khởi chạy để phục vụ từng Intent riêng biệt. Do đó, số lượng các Activity này được khởi chạy trên một thiết bị là không giới hạn.

  • Trong cùng một ứng dụng, các Activity sẽ được đặt trong một ngăn xếp. Chẳng hạn tôi tạo một app tên launchMode gồm 4 standard Activity như này:

    <activity
        android:name=".c"
        android:exported="false"
        android:launchMode="standard"/>
    <activity
        android:name=".b"
        android:exported="false"
        android:launchMode="standard" />
    <activity
        android:name=".a"
        android:exported="false"
        android:launchMode="standard"/>
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="standard">
        ```
            
    

Thuộc tính launchMode có thể bỏ, không cần phải viết vào đối với trường hợp standard. Tại mỗi một Activity tôi tạo ra sự kiện có thể bật lên một Activity khác theo một vòng như này: MainActivity -> a -> b -> c -> a ... Các Activity sẽ được tạo ra liên tục theo vòng vừa rồi khi ta sử dụng sự kiện đó, và bây giờ để quay trở lại MainActivity ta cần bấm nút back đi qua hết tất cả các Activity đã tạo ra trước đó theo cơ chế ngăn xếp, cho đến tận đáy của ngăn xếp là MainActivity. Mọi thứ đều bình thường đúng không.

  • Khi chúng ta thực hiện bật ứng dụng chứa các standard Activity từ một ứng dụng khác, chúng ta sẽ thấy điều dị lạ ở đây.
    • Tôi thực hiện tạo 2 app đều chứa các standard Activity, trong đó app Test2 dùng để thực hiện bật lên app launchMode:
      image.png
    • Tiếp theo bật app Test2 lên và đây là màn hình app Test2, khi này tôi cần click vào TextView để bật app launchMode lên:
      image.png
    • Ok, đây là màn hình của app launchMode sau khi được bật lên:
      image.png
    • Giờ chúng ta hãy thử xem các tác vụ có trong máy sau khi vừa bật 2 app lên:
      image.png
      Các bạn có đang thấy nổ não như tôi không ? trong khi tên app đang hiển thị ở tác vụ là Test2 nhưng màn hình của app Test2 đang hiển thị là màn hình của app launchMode 😄. Tuy nhiên thì đó chỉ là cách xử lý của Android pre-Lollipop (tiền Lollipop), còn trên Lollipop hoặc sau Lollipop mọi thứ đều diễn ra bình thường, Task Manager vẫn sẽ hiện thị lên 2 app như những gì chúng ta tưởng tượng ban đầu.

3. Chế độ singleTop

  • Chế độ tiếp theo là singleTop . Nó hoạt động gần giống như chế độ standard , có nghĩa là các Activity của singleTop có thể được tạo nhiều như chúng ta muốn. Chỉ có sự khác biệt là nếu đã có một Activity có cùng loại ở đầu ngăn xếp trong tác vụ (task) người gọi, sẽ không có bất kỳ Activity mới nào được tạo, thay vào đó, một Intent sẽ được gửi đến một Activity hiện có thông qua phương thức onNewIntent().
    image.png
  • Trong chế độ SingleTop, bạn phải xử lý Intent đến trong cả hai phương thức onCreate() và onNewIntent() để nó có thể hoạt động trong mọi trường hợp.
  • Một trường hợp điển hình sử dụng chế độ này là chức năng Tìm kiếm. Hãy nghĩ đến việc tạo một hộp tìm kiếm sẽ dẫn bạn đến một SearchActivity để xem kết quả tìm kiếm. Để có trải nghiệm tốt hơn, thông thường, chúng tôi luôn đặt hộp tìm kiếm trong trang kết quả tìm kiếm để cho phép người dùng thực hiện một tìm kiếm khác mà không cần nhấn lại.
  • Bây giờ lại tưởng tượng, nếu chúng ta luôn khởi chạy SearchActivity mới để cung cấp kết quả tìm kiếm mới, thì chúng ta sẽ có 10 Activity mới cho 10 lần tìm kiếm. Sẽ rất kỳ lạ khi chúng ta phải nhấn trở lại 10 lần để đi qua các Activity kết quả tìm kiếm trước đó rồi mới quay lại Activity gốc được.
  • Thay vào đó, nếu có SearchActivity trên đầu ngăn xếp, chúng ta nên gửi một Intent đến cái Activity hiện có và để nó cập nhật kết quả tìm kiếm là được. Bây giờ chúng ta chỉ có một SearchActivity được đặt trên đầu ngăn xếp và bạn có thể chỉ cần nhấn nút quay lại 1 lần duy nhất để quay lại Activity gốc. Thật tuyệt vời đấy.
  • Dù sao, singleTop làm việc với cùng một nhiệm vụ chỉ với người gọi. Nếu bạn mong đợi một Intent được gửi đến một Activity hiện có được đặt trên bất kỳ Nhiệm vụ nào khác, có lẽ bạn thất vọng khi biết rằng nó không hoạt động theo cách đó. Trong trường hợp Intent được gửi từ một ứng dụng khác đến singleTop Activity, một Activity mới sẽ được khởi chạy theo cùng với Chế độ khởi chạy standard (pre-Lollipop: được đặt trên đầu Tác vụ của người gọi, Lollipop: một Tác vụ mới sẽ được tạo) .
  • Tóm lược lại, singleTop có cơ chế ngăn xếp giống standard nhưng nó khác standard ở chỗ không tạo mới liên tục các phiên bản của 1 Activity trên cùng của ngăn xếp và sử dụng thêm onNewIntent() để thực hiện điều đó, tiếp tục ví dụ để thông não:
    • Tại app LaunchMode, tôi vẫn có 4 Activity như vừa nãy tuy nhiên lúc này các trong các thuộc tính launchMode không còn là standard nữa mà chúng được chuyển qua singleTop:
      image.png

4. Chế độ singleTask

  • Chế độ này khá khác so với standard và singleTop. Một Activity với chế độ khởi chạy singleTask chỉ được phép có một phiên bản trong hệ thống (hay còn gọi là Singleton). Nếu có một Activity tồn tại trong hệ thống, toàn bộ Activity giữ nhiệm vụ sẽ được chuyển lên trên cùng trong khi Intent sẽ được gửi thông qua phương thức onNewIntent(). Nếu không, Activity mới sẽ được tạo và đặt trong Nhiệm vụ thích hợp.

  • Khi làm việc trong cùng một ứng dụng

    • Nếu chưa có singleTask Activity nào tồn tại trong hệ thống, thì cá thể mới sẽ được tạo và chỉ cần đặt trên đầu ngăn xếp trong cùng một Tác vụ.
      image.png

    • Nhưng nếu có một Activity tồn tại, tất cả các Activity được đặt phía trên singleTask Activity đó sẽ tự động bị hủy theo cách thích hợp (vòng đời) để làm cho một Activity mà chúng ta muốn xuất hiện trên đầu ngăn xếp. Đồng thời, một Intent sẽ được gửi đến singleTask Activity thông qua phương thức onNewIntent().

    • Từ thử nghiệm, nó dường như không hoạt động như mô tả. Một singleTask Activity vẫn xếp chồng lên trên ngăn xếp Activity của Task như chúng ta có thể thấy từ dumpsys activity hiển thị.

    • Nếu bạn muốn để singleTask Activity hoạt động như mô tả trong tài liệu: hãy tạo một Nhiệm vụ mới và đặt một Activity làm Activity gốc. Bạn cần gán thuộc tính taskAffinity cho singleTask Activity kiểu:

      <activity
          android:name=".MainActivity"
          android:exported="true"
          android:launchMode="singleTask"
          android:taskAffinity="">
      
    • Đây là kết quả khi chúng ta khởi chạy SingleTask Activity.

  • khi cộng tác với một ứng dụng khác

    • Sau khi một Intent được gửi từ một ứng dụng khác và chưa có bất kỳ Activity nào được tạo trong hệ thống, thì Task mới sẽ được tạo với Activity mới được đặt làm Activity gốc.

    • Trừ khi tồn tại một Task của ứng dụng sở hữu singleTask Activity đang gọi, thay vào đó, một Activity mới được tạo sẽ được đặt lên trên nó.

    • Trong trường hợp có một Activity tồn tại trong bất kỳ Tác vụ nào, toàn bộ Nhiệm vụ sẽ được chuyển lên trên cùng và mọi Activity đơn lẻ được đặt phía trên singleTask Activity sẽ bị hủy theo vòng đời. Nếu nút quay lại được nhấn, người dùng phải đi qua các Activity trong ngăn xếp trước khi quay lại Tác vụ của người gọi.
      image.png

    • Một trường hợp sử dụng chế độ này là bất kỳ Activity đầu vào nào, ví dụ như trang Hộp thư đến của Khách hàng qua Email hoặc Dòng thời gian của Mạng xã hội. Những Activity đó không được thiết kế để có nhiều hơn một phiên bản nên singleTask sẽ thực hiện công việc đó một cách hoàn hảo. Dù sao, bạn phải sử dụng chế độ này một cách khôn ngoan vì các Activity khác có thể bị hủy mà không cần sự chấp thuận của người dùng.

5. Chế độ singleInstance

  • Chế độ này khá giống với singleTask, chỉ có một phiên bản Activity duy nhất có thể tồn tại trong hệ thống. Sự khác biệt là Task giữ Activity này chỉ có thể có một Activity, một Activity duy nhất. Nếu một Activity khác được gọi từ loại Activity này, thì một Task mới sẽ tự động được tạo để đặt Activity mới đó. Tương tự như vậy, nếu SingleInstance Activity được gọi, Task mới sẽ được tạo để đặt Activity.
  • Dù sao thì kết quả cũng khá kỳ lạ. Từ thông tin được cung cấp bởi dumpsys, có vẻ như có hai Task trong hệ thống nhưng chỉ có một Task xuất hiện trong Trình quản lý Tác vụ (Task Manager) tùy thuộc vào đó là Task mới nhất được chuyển lên trên cùng. Do đó, mặc dù có một Tác vụ vẫn đang hoạt động ở chế độ nền nhưng chúng tôi không thể chuyển nó về nền trước.
  • Đây là điều đã xảy ra khi SingleInstance Activity được gọi trong khi đã có một số Activity tồn tại trong ngăn xếp.
    image.png
  • Nhưng đây là những gì chúng ta thấy từ Task Manager.
    image.png
  • Vì Task này chỉ có thể có một Activity, nên chúng tôi không thể quay lại Task số 1 nữa. Cách duy nhất để làm như vậy là khởi chạy lại ứng dụng từ launcher nhưng thay vào đó, có vẻ như SingleInstance Task sẽ bị ẩn trong nền.
  • Dù sao, có một số giải pháp cho vấn đề này. Giống như chúng ta đã làm với singleTask Activity, chỉ cần gán thuộc tính taskAffinity cho SingleInstance Activity để bật nhiều Tác vụ trên Task Manager.
  • Chế độ này hiếm khi được sử dụng. Một số trường hợp sử dụng thực sự là Activity cho Trình khởi chạy hoặc ứng dụng mà bạn chắc chắn 100% chỉ có một Activity. Dù sao tôi cũng khuyên bạn không nên sử dụng chế độ này trừ khi nó thực sự cần thiết.

6. Intent Flags

  • Bên cạnh việc chỉ định trực tiếp chế độ khởi chạy trong AndroidManifest.xml, chúng ta cũng có thể chỉ định nhiều hành vi hơn thông qua thứ được gọi là Intent Flags , ví dụ:
    image.png
  • Sẽ khởi chạy Standard Activity với điều kiện Chế độ khởi chạy singleTop. Có khá nhiều Flags bạn có thể dùng. Bạn có thể tìm thêm về nó tại Intent.

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


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.