12/08/2018, 12:23

Tìm hiểu về launch mode trong lập trình Android

I. Tìm hiểu chung 1. Task Trước khi tìm hiểu về Launch Mode Android thì chúng ta sẽ tìm hiểu trước thuật ngữ " Task " (nhiệm vụ) Task về bản chất là 1 stack chứa các đối tượng của activity Khi bật ứng dụng thì 1 task mới được tạo ra và nó là task gốc của ứng dụng Android có ...

I. Tìm hiểu chung

1. Task

Trước khi tìm hiểu về Launch Mode Android thì chúng ta sẽ tìm hiểu trước thuật ngữ "Task" (nhiệm vụ)

  • Task về bản chất là 1 stack chứa các đối tượng của activity

  • Khi bật ứng dụng thì 1 task mới được tạo ra và nó là task gốc của ứng dụng

Android có thể giữ nhiều task cùng một thời điểm (background) và chỉ hiển thị một task duy nhất cho người dùng tương tác (foreground).

Bạn có thể nhấn và giữ nút "Home" để xem các danh sách task đang thực thi của thiết bị. Mỗi một task này đại diện cho một ứng dụng đã được bật và giữ ở background. Khi chúng ta click vào các task này thì thiết bị sẽ tiến hành mở ứng dụng tương ứng lên foreground cho chúng ta.

2. Launch mode của android

Launch mode cho phép chúng ta khởi tạo activity mới hoặc gọi actiivty tương ứng với task đang thực thi của ứng dụng. Activity launch mode có 4 giá trị, và chúng ta thường khai báo trong file AndroidManifest.xml của ứng dụng:

<activity android:launchMode = ["standard" | "singleTop" | "singleTask" | "singleInstance"] ../>

"standard": là giá trị mặc định nếu chúng ta không khai báo launch mode cho activity. Và chúng ta chia 4 giá trị launch mode của activity thành 2 nhóm:

  • "standard & singleTop" dùng để khởi tạo nhiều đối tượng activity và các đối tượng này ở trong cùng một task.

  • "singleTask & singleInstance" thì đối tượng activity được khởi tạo theo pattern singleton (tham khảo : http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/). Khi activity được khởi tạo với launch mode này thì sẽ trở thành activity gốc của 1 task mới.

II. Ứng dụng thực tế

1. Standard

Đây là giá trị mặc định launch mode khi không khai báo và thường là chúng ta sẽ sử dụng trường hợp này.

Với giá trị này thì chúng ta có thể tạo nhiều đối tượng instance của activity, những instance này có thể thêm vào trong cùng 1 task hoặc thêm vào ở các task khác nhau của ứng dụng.

2. singleTop

Cái này chỉ khác với standard ở chỗ nếu instance của activity đã tồn tại ở trên cùng của task (last) và hệ thống dẫn intent đến activity này thì hệ thống sẽ không tạo đối tượng instance mới của activity và nó sẽ được chỉ định vào hàm onNewIntent() của activity thay vì khởi tạo một đối tượng activity mới. Ví dụ ở module oauth của Twitter khi tích hợp vào các ứng dụng bên thứ 3:

<activity android:name=".NewsDetailActivity" android:launchMode="singleTop">
      <intent-filter>
          <action android:name="android.intent.action.VIEW"></action>
          <category android:name="android.intent.category.DEFAULT"></category>
          <category android:name="android.intent.category.BROWSABLE"></category>
          <data android:scheme="oauth" android:host="twitt"></data>
      </intent-filter>
 </activity>

Và trong activity tích hợp chúng ta sẽ dựa theo đoạn code bên dưới để bắt đầu quá trình oAuth để hướng trình duyệt tới trang xác thực tài khoản của Twitter:

private void twitterOAuth() {
    try {
        System.setProperty("twitter4j.http.useSSL", "false");
        System.setProperty("twitter4j.oauth.consumerKey", getString(R.string.twitter_oauth_consumer_key));
        System.setProperty("twitter4j.oauth.consumerSecret", getString(R.string.twitter_oauth_consumer_secret));
        System.setProperty("twitter4j.oauth.requestTokenURL", "http://api.twitter.com/oauth/request_token");
        System.setProperty("twitter4j.oauth.accessTokenURL", "http://api.twitter.com/oauth/access_token");
        System.setProperty("twitter4j.oauth.authorizationURL", "http://api.twitter.com/oauth/authorize");

        // get the instance
        Twitter twitter = new TwitterFactory().getInstance();
        twitter4j.auth.RequestToken requestToken = twitter.getOAuthRequestToken(CALLBACK_URL);

        //save the request token and secret
        prefsManager.saveTwitterRequestToken(requestToken.getToken(), requestToken.getTokenSecret());
        //save other data
        persistNewsDatas();

        //start the browser activity
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(requestToken.getAuthenticationURL()));
        startActivity(intent);
    } catch (Exception e) {
        Log.e(TAG, e.getMessage(), e);
        Toast.makeText(NewsDetailActivity.this, "Fail to connect to twitter now, please try later.",
                    Toast.LENGTH_SHORT).show();
    }
}

Và nếu người dùng đến activity này từ task hiện thời (tạm gọi là Task #1), thì một đối tượng mới của NewsDetailActivity sẽ được đây lên top của task. Và bây giờ thì người dùng sẽ tiến hành quá trình xác thực và hệ điều hành Android sẽ mở trình duyệt mặc định của người dùng và tới trang yêu cầu xác thực của Twitter. "singleTask" mode ở đây là tạo một task mới cho trình duyệt (browser activity) (tạm gọi là Task #2). Sau các hoạt động xác thực và trở lại ứng dụng thì NewDetailActivity vẫn ở trên cùng (top) của Task #1.

Sau khi người dùng kết thúc quá trình xác thực ở trang web Twitter, thông qua callback url (một đường link khi tạo ứng dụng trên twitter để gọi về ứng dụng tích hợp oauth://twitter) sẽ gọi lại ứng dụng. Và do NewsDetailActivity được khai báo để nhận dữ liệu từ browser nên hệ thống sẽ mở lại ứng dụng ở NewsDetailActivity. Và NewsDetailActivity được tạo với launch mode "singleTop" nên nó sẽ được dùng lại ở Task #1 (vì ở trên top của task này từ trước), và ứng dụng sẽ gọi qua method onNewIntent() của activity để chúng ta thực hiện bước cuối cùng của việc xác thực. Và đây là đoạn code để làm điều đó:

protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    Uri uri = intent.getData();
    if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
        String verifier = uri.getQueryParameter("oauth_verifier");
        try {
            //load other persisted data goes here
            ....
            //get the request-token from prefs and request the access token
            TwitterFactory twitter = new TwitterFactory().getInstance();
            requestToken = prefsManager.getRequestToken();
            prefsManager.clearTwitterRequestToken();
            twitter4j.auth.AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, verifier);
            // save the access token
            prefsManager.saveTwitterOauth(accessToken.getToken(), accessToken.getTokenSecret(), accessToken
                            .getScreenName());
            //other logics to do the post to twitter
            ....
         } catch (Exception e) {
             Log.e(TAG, e.getMessage(), e);
         }
    }
}

3. singleTask

Với giá trị này thì luôn luôn tạo một task mới và một đối tượng khởi tạo của activity sẽ được thêm vào làm gốc của task đó.

Tuy nhiên nếu đối tượng activity này đã tồn tại ở bất kỳ task nào khác rồi thì hệ thống sẽ gọi activity đó thông qua phương thức onNewIntent(). Với mode này thì nhiều đối tượng khởi tạo của acitivity có thể được thêm vào cùng 1 task và khi người dùng click BACK key từ singleTask activity hệ thống sẽ đưa người dùng về activity trước đó.

Mode này thích hợp trong các ứng dụng sử dụng activity như một cầu nối (TAB, ...). Ví dụ trong vấn đề tab-based của ứng dụng Twitter, chúng ta có thể khai báo activity chính (mặc định) TabAcitivity với mode này. Và chúng ta cũng có thể sử dụng mode này nếu bạn chỉ muốn 1 đối tượng của 1 activity được tạo ra mà thôi. Một ví dụ điển hình khác là ứng dụng trình duyệt trên device.

Chúng ta sẽ lấy ứng dụng Twitter-client-application làm ví dụ, activity chính của app là TabActivity và chúng ta có 3 tab phụ khác là tweets, replies, message. Và chúng ta muốn tự động cập nhật lại chức năng thông báo suốt quá trình ứng dụng đang chạy.

Trong file AndroidManifest.xml chúng ta khai báo:

<activity android:name=".MainTabActivity" android:launchMode="singleTask" android:alwaysRetainTaskState="true" android:windowSoftInputMode="adjustPan">
</activity>

Mục đích chúng ta là báo những thông báo mới cho các tab tweet/replies/messages, và chúng va hi vọng rắng khi người dùng click vào mục thông báo (notification) của hệ thống thì hệ thống sẽ mở TabActivity và di chuyển tới tab tương ứng của thông báo. (Chúng ta giả xử rằng ứng dụng đang bật khi người dùng click vào notification trên thanh status của thiết bị). Bởi vì launch mode của MainTabActivity là singleTask, hệ thống sẽ mở MainTabActivity và gọi hàm onNewIntent(). Và trong phương thức này chúng ta sẽ quyết định mở tab nào tương ứng với data mà intent mang theo.

4. singleInstance

Cái này cũng tương tự như "singleTask" ngoại từ việc không có nhiều đối tượng của cùng 1 activity trong cùng 1 task. Điều này có nghĩa là activity với launch mode này luôn luôn chỉ có 1 đối tượng của 1 activity trược task. Mode này rất đặc biệt và thường chỉ dùng trong các ứng dụng mà toàn ứng dụng chỉ sử dụng 1 activity duy nhất. (1 MainActivity với các Fragment).

III. Tổng kết

Hiểu về launch mode giúp bạn thiết kế và điều hướng các activity tốt hơn trong các trường hợp yêu cầu đặc biệt. Và ở đây chúng ta chỉ dừng lại ở việc thảo luận khai báo các launch mode trong file AndroidManifest. Android launch mode cũng có thể khai báo thông qua FLAG của intent như FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TOP và FLAG_ACTIVITY_SINGLE_TOP.

Ứng dụng tham khảo:

  • https://play.google.com/store/apps/details?id=com.novoda.demos.activitylaunchmode
  • source: https://github.com/gnorsilva/Activities-LaunchMode-demo

Bài viết tham khảo:

  • https://www.mobomo.com/2011/06/android-understanding-activity-launchmode/
  • http://developer.android.com/guide/components/tasks-and-back-stack.html
  • http://blog.akquinet.de/2011/02/25/android-activities-and-tasks-series-–-activity attributes/
0