ในบางแอป การมีแค่ Task เดียวก็อาจจะไม่ตอบโจทย์การใช้งานได้ทุกรูปแบบ ทำให้ระบบแอนดรอยด์ออกแบบให้แอปสามารถมี Task ได้มากกว่าหนึ่งตัวหรือที่เรียกว่า Multiple Task

บทความในชุดนี้

Multiple Task → Concurrent Documents

ถึงแม้ว่า Task มากกว่า 1 ตัวจะเรียก Multiple Task ก็ตาม แต่นักพัฒนาจะเรียกกันว่า Concurrent Documents ซึ่งเป็นชื่อที่แอนดรอยด์กำหนดขึ้นมาอย่างเป็นทางการนับตั้งแต่ Android 5.0 Lollipop ที่เริ่มรองรับ Multiple Task อย่างจริงจังมากขึ้น

สำหรับ Task ใด ๆ ก็ตามที่แอปต้องการสร้างแยกออกมาสำหรับการทำงานบางอย่าง ตามนิยามของ Concurrent Documents เราจะเรียก Task เหล่านั้นว่า Document ยกตัวอย่างเช่น

  • สร้างบันทึกใหม่ - แอปจดบันทึก
  • สร้างร่างจดหมาย - แอปอีเมล
  • เปิดหน้าต่างใหม่ - แอป Web Browser

ดังนั้นเมื่อพูดถึง Task และ Document ในหัวข้อนี้จึงหมายถึงสิ่งเดียวกัน

Task บน Multi-window Mode

ในการใช้งานทั่วไปที่แอปแสดงเต็มหน้าจอ เมื่อมี Task ใหม่ถูกสร้างขึ้นมา ก็จะแสดงแทนที่ตัวเก่า โดยที่ Recents Screen จะแสดง Task แยกกันถึงแม้ว่าจะเป็นแอปตัวเดียวกัน

แต่การทำงานบน Multi-window Mode จะเห็นได้ชัดเจนว่าแอปแต่ละหน้าต่างคือ Task คนละตัวกัน ถึงแม้ว่าจะมาจากแอปตัวเดียวกันก็ตาม

Split-screen Mode

โหมดที่จะแบ่งหน้าจอออกเป็น 2 ส่วนและสามารถแสดง 2 แอปได้พร้อม ๆ กัน

  • แอปที่มี Task เดียว จะแสดงได้แค่ที่ฝั่งใดฝั่งหนึ่งเท่านั้น
  • แอปที่มีหลาย Task แต่ละ Task สามารถกำหนดให้แสดงผลที่คนละฝั่งหน้าจอได้ ทำให้เปิดแอปเดียวกันในหน้าจอทั้ง 2 ฝั่งได้ เพราะเป็นคนละ Task กัน

Freeform Mode

โหมดที่จะแบ่งแอปเป็นหน้าต่างแยกกันอย่างอิสระ ทำให้ผู้ใช้สามารถใช้งานหลายแอปได้พร้อมกัน

  • แอปที่มี Task เดียว จะแสดงได้แค่หน้าต่างเดียวเท่านั้น
  • แอปที่มีหลาย Task จะแสดงแต่ละ Task เป็นหน้าต่างที่แยกกันอย่างอิสระ ทำให้เวลาสร้าง Task ใหม่ จะเป็นการสร้างหน้าต่างใหม่ขึ้นมานั่นเอง

การสร้าง Task สำหรับ Concurrent Documents

สำหรับการสร้าง Task เพื่อให้แอปรองรับ Concurrent Documents จะมีอยู่ 2 วิธีด้วยกัน

  • กำหนดผ่าน Attribute ของ Activity ใน Android Manifest
  • กำหนดผ่าน Flag ของ Intent

กำหนดผ่าน Attribute ของ Activity ใน Android Manifest

นักพัฒนาสามารถกำหนด android:documentLaunchMode ใน Android Manifest ให้กับ Activity ที่ต้องการให้ทำงานแบบ Concurrent Documents ได้เลย โดยจะมีค่าให้กำหนดทั้งหมด 4 แบบด้วยกัน

  • intoExisting - ระบบแอนดรอยด์จะค้นหาก่อนว่ามี Task ใดที่มี Activity ดัวนั้นอยู่หรือไม่ ถ้าไม่มีก็จะสร้าง Task พร้อมกับ Activity ขึ้นมาใหม่ แต่ถ้ามีอยู่แล้วก็จะเคลียร์ Task และสั่งให้เริ่มทำงานใหม่อีกครั้ง ทำให้ Activity กลับมาทำงานโดยส่งข้อมูลผ่าน onNewIntent แทน
การทำงานของ infoExisting จะเหมือนกับ Intent ที่กำหนดค่า FLAG_ACTIVITY_NEW_DOCUMENT
  • always - ระบบแอนดรอยด์จะสร้าง Task และ Activity ขึ้นมาใหม่เสมอ ถึงแม้ว่าจะเคยสร้าง Activity ตัวนั้นไว้ใน Task อื่นมาก่อนก็ตาม
การทำงานของ always จะเหมือนกับ Intent ที่กำหนดค่า FRAG_ACTIVITY_NEW_DOCUMENT และ FLAG_ACTIVITY_MULTIPLE_TASK คู่กัน
  • never - เป็นการบังคับให้ระบบแอนดรอยด์สร้าง Activity ขึ้นมาบน Task เดิมเสมอ ถึงแม้ว่าจะมีการกำหนด Flag อย่าง FLAG_ACTIVITY_NEW_DOCUMENT ก็ตาม
  • none - เป็นค่า Default ของ Activity ที่ระบบแอนดรอยด์จะสร้าง Task ขึ้นมาใหม่ก็ต่อเมื่อมีการกำหนด FLAG_ACTIVITY_NEW_TASK เท่านั้นกำหนดผ่าน Flag ใน Intent

ดังนั้นในกรณีที่ต้องการสร้าง Activity บน Task ใหม่ตลอดก็ให้กำหนดด้วย always แต่ถ้าอยากให้สร้างขึ้นมาแล้วใช้ตัวเดิมตลอดก็ให้ใช้ infoExisting แทนนั่นเอง

กำหนดผ่าน Flag ของ Intent

สำหรับ Flag ของ Intent ที่เกี่ยวข้องกับการทำงานของ Concurrent Documents จะมีอยู่แค่ 3 ตัวด้วยกัน คือ FLAG_ACTIVITY_NEW_DOCUMENT, FLAG_ACTIVITY_MULTIPLE_TASK และ FLAG_ACTIVITY_LAUNCH_ADJACENT

  • FLAG_ACTIVITY_NEW_DOCUMENT - ระบบแอนดรอยด์จะค้นหาก่อนว่ามี Task ใดที่มี Activity ตัวนั้นอยู่หรือไม่ ถ้าไม่มีก็จะสร้าง Task พร้อมกับ Activity ขึ้นมาใหม่ แต่ถ้ามีอยู่แล้วก็จะเคลียร์ Task และสั่งให้เริ่มทำงานใหม่อีกครั้ง ทำให้ Activity กลับมาทำงานโดยส่งข้อมูลผ่าน onNewIntent แทน
การทำงานจะเหมือนกับ intoExisting ของ android:documentLaunchMode
  • FLAG_ACTIVITY_NEW_DOCUMENT + FLAG_ACTIVITY_MULTIPLE_TASK - ระบบแอนดรอยด์จะสร้าง Task และ Activity ขึ้นมาใหม่เสมอ ถึงแม้ว่าจะเคยสร้าง Activity ตัวนั้นไว้ใน Task อื่นมาก่อนก็ตาม
การทำงานจะเหมือนกับ always ของ android:documentLaunchMode
  • FLAG_ACTIVITY_LAUNCH_ADJACENT - ใช้ร่วมกับ FLAG_ACTIVITY_NEW_DOCUMENT ใน Split-screen Mode ของ Multi-window Mode เพื่อให้ระบบแอนดรอยด์สร้าง Task ตัวใหม่ขึ้นมาที่อีกฝั่งของหน้าจอ

นักพัฒนาใช้วิธีไหนดี ?

การสั่งงานสำหรับ Concurrent Documents จะเหมือนกับเรื่อง Launch Mode ที่จะขึ้นอยู่กับว่านักพัฒนาต้องการแบบไหน เพราะแต่ละแอปก็จะมีรูปแบบในการใช้งานที่แตกต่างกันออกไป

ถ้าต้องการให้ Activity บางตัวรองรับ Concurrent Documents เสมอ การกำหนดค่าผ่าน android:documentLaunchMode ก็จะมั่นใจได้ว่าการทำงานจะเป็นแบบเดียวกันเสมอ โดยไม่ต้องกำหนด Flag ใน Intent ให้เสียเวลา

แต่ถ้าแอปออกแบบมาให้ Activity นั้น ๆ สามารถอยู่บน Single Task หรือ Concurrent Documents ก็ได้ การกำหนดผ่าน Flag ของ Intent ก็จะมีความยืดหยุ่นมากกว่า

จัดการกับ Task ในแอปด้วย ActivityManager

สำหรับการทำงานแบบ Concurrent Documents ที่อาจจะทำให้การทำงานของ Task มีความซับซ้อนมากขึ้น นักพัฒนาสามารถใช้ ActivityManager เพื่อควบคุมการทำงานบางอย่างสำหรับ Task ที่มีอยู่ทั้งหมดในแอปได้

val context: Context = /* ... */
val activityManager: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val appTasks: List<ActivityManager.AppTask> = activityManager.appTasks

โดยจะมีคำสั่งอย่าง

  • finishAndRemoveTask() เพื่อทำลาย Task นั้น ๆ ทิ้ง
  • moveToFront() เพื่อสั่งให้ Task นั้น ๆ เป็น Foreground Task
  • ฯลฯ

สามารถเข้าไปดูคำสั่งทั้งหมดของ AppTask ได้ที่ ActivityManager.AppTask [Android Developers]

ActivityManager.AppTask | Android Developers

เรื่องอื่น ๆ ที่ควรรู้

  • ในการทำ Concurrent Documents ควรออกแบบ User Flow ให้มีความซับซ้อนน้อยที่สุดเท่าที่ทำได้ (เช่น การแยก Flow ของการทำงานที่ต้องแยก Task ให้ชัดเจนและจบในตัว) เพื่อลดโอกาสที่จะเกิด Behavior นอกเหนือจากที่ต้องการ
  • นักพัฒนาสามารถกำหนดได้ว่าจะให้ Task ในแอปแสดงอยู่บน Recents Screen ได้สูงสุดกี่ Task โดยกำหนดผ่าน android:maxRecents ของ <application> ใน Android Manifest สามารถกำหนดได้สูงสุดถึง 50 Task (25 Task สำหรับเครื่องที่มี RAM น้อย)

แหล่งข้อมูลอ้างอิง