Task และ Back Stack ตอนที่ 6 - Activity Launch Mode [2/2]

สำหรับบทความนี้ไม่ขอเกริ่นอะไรมาก เพราะเป็นบทความที่ต่อเนื่องมาจากบทความก่อนหน้า ที่อธิบายเรื่องราวของ Activity Launch Mode โดยเฉพาะ

เจ้าของบล็อกขอเรียก Activity Launch Mode แบบสั้น ๆ ว่า Launch Mode

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

จากความเดิมในตอนที่แล้ว

ในบทความก่อนหน้านี้ เจ้าของบล็อกก็ได้พูดถึงหน้าที่ของ Launch Mode และวิธีการกำหนดผ่าน android:launchMode ใน Android Manifest กันไปแล้ว ในบทความนี้ก็จะพูดถึงการกำหนดค่า Launch Mode ให้กับ Activity ผ่าน Intent Flag กันต่อ

Launch Mode ใน Flag ของ Intent

สำหรับการกำหนดค่า Launch Mode ผ่าน Flag ของ Intent นั้นจะแตกต่างจากการกำหนดผ่าน android:launchMode ใน Android Manifest ตรงที่นักพัฒนาสามารถกำหนด Flag ที่เกี่ยวข้องกับ Launch Mode ได้มากกว่าหนึ่งตัว

setFlags

val intent = Intent(/* ... */).apply {
    flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
}

addFlag

val intent = Intent(/* ... */).apply {
    addFlag(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    addFlag(Intent.FLAG_ACTIVITY_NEW_TASK)
}

ถึงแม้ว่านักพัฒนาจะกำหนด Flag สำหรับ Launch Mode ได้หลายตัวพร้อมกัน แต่ที่ใช้กันจริง ๆ จะมีอยู่ไม่กี่รูปแบบเท่านั้น

และนอกจากนี้การทำงานของ Flag เหล่านี้จะสัมพันธ์กับการทำงานของ Task Affinity ด้วย ซึ่งจะพูดถึงในบทความ Task และ Back Stack ตอนที่ 7 - Task Affinity

สำหรับ Flag ใน Intent ที่เกี่ยวข้องกับ Launche Mode จะมีดังนี้

  • FLAG_ACTIVITY_NEW_TASK - ถ้ายังไม่มี Task ไหนที่มี Activity ตัวดังกล่าว ระบบแอนดรอยด์จะสร้าง Task ขึ้นมาใหม่ แต่ถ้ามีอยู่แล้วก็จะเปลี่ยนให้ Task นั้นกลายเป็น Foreground Task แทน และส่งข้อมูลให้ Activity ตัวนั้นผ่าน onNewIntent
  • FLAG_ACTIVITY_CLEAR_TASK - ระบบแอนดรอยด์จะเคลียร์ Task ทำให้ Activity ที่อยู่ในนั้นถูกทำลายทิ้งทั้งหมด แล้วสร้าง Activity ที่ต้องการขึ้นมา โดยจะส่งผลให้ Activity ตัวนั้นกลายเป็น Root Activity ใน Task นั้นแทน
  • FLAG_ACTIVITY_SINGLE_TOP - ถ้ามี Activity ตัวเดียวกันที่อยู่ข้างบนสุดของ Back Stack แทนที่จะสร้างขึ้นมาใหม่ ก็จะส่งข้อมูลทาง onNewIntent ให้ Activity ตัวนั้นแทน ซึ่งจะคล้ายกับการกำหนด singleTop ใน android:launchMode นั่นเอง
  • FLAG_ACTIVITY_CLEAR_TOP - ถ้ามี Activity ตัวนั้นอยู่ใน Task ใด ๆ แทนที่จะสร้าง Activity ขึ้นมาใหม่ ระบบแอนดรอยด์จะทำลาย Activity ใน Activity Stack ที่อยู่เหนือกว่า Activity ตัวนั้นทิ้ง เพื่อให้ Activity ตัวนั้นขึ้นมาอยู่ข้างบนสุด และส่งข้อมูลให้ทาง onNewIntent()
  • FLAG_ACTIVITY_REORDER_TO_FRONT - ถ้ามี Activity ตัวนั้นอยู่ใน Activity Stack จะย้ายขึ้นมาอยู่ข้างบนสุดของ Activity Stack แทน แต่ถ้าไม่มีก็จะสร้างใหม่ตามปกติ
  • FLAG_ACTIVITY_LAUNCH_ADJACENT - ใช้ร่วมกับ Multi-window Mode เพื่อให้ Task ที่สร้างขึ้นมาใหม่ไปแสดงบนหน้าจออีกฝั่ง
  • FLAG_ACTIVITY_MULTIPLE_TASK - สำหรับสร้าง Activity ขึ้นบน Task ตัวใหม่ โดยจะใช้ร่วมกับ Flag ตัวอื่น ๆ อีกที
  • FLAG_ACTIVITY_NEW_DOCUMENT - ระบบแอนดรอยด์จะสร้าง Task ตัวใหม่ตามเงื่อนไขการทำงานของ Concurrent Documents ซึ่งจะพูดถึงอีกทีในบทความสุดท้าย

โดยบางตัวจะไม่ได้พูดถึง เพราะไม่ค่อยได้ใช้กัน และสำหรับ FLAG_ACTIVITY_NEW_TASK จะขึ้นอยู่กับการกำหนดค่า Task Affinity ใน Activity แต่ละตัวด้วย ซึ่งจะอธิบายในบทความถัดไป

Flag แต่ละตัวใช้ตอนไหนบ้าง?

ถึงแม้ว่า Flag แต่ละตัวจะส่งผลต่อการทำงานของ Launch Mode ที่แตกต่างกัน แต่ในการนำไปใช้งานจริงก็จะมีหลากหลายรูปแบบ ดังนั้นขออธิบายด้วยรูปแบบการนำไปใช้งานจริงแทนดีกว่า

FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TASK

Flag สองตัวนี้จะใช้คู่กันเพื่อให้ระบบแอนดรอยด์เคลียร์ข้อมูลใน Task ใด ๆ ที่เคยทำงานอยู่แล้วสร้าง Activity ขึ้นมาบน Task ตัวใหม่

0:00
/

จึงทำให้การใช้ Flag แบบนี้เหมาะกับการกลับไปที่หน้าหลักของแอป โดยเคลียร์ Activity Stack ก่อนหน้านั้นทิ้งและเริ่มทุกอย่างใหม่ทั้งหมด

วิธีนี้อาจจะดูคล้ายกับ android:launchMode="singleTask แต่จะมีจุดแตกต่างกันตรงที่ การกำหนดผ่าน Flag แบบนี้จะทำให้ Activity ถูกสร้างขึ้นมาใหม่ และข้อมูล Intent จะส่งให้ตามปกติ ไม่ได้ส่งผ่าน onNewIntent แบบ Single Task

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

FLAG_ACTIVITY_SINGLE_TOP + FLAG_ACTIVITY_CLEAR_TOP

Flag สองตัวนี้จะใช้คู่กันเพื่อให้ระบบแอนดรอยด์สร้าง Activity คล้ายกับ android:launchMode="singleTop โดยที่

  • ถ้ายังไม่มี Activity นั้น ๆ อยู่ใน Activity Stack – ก็จะสร้างขึ้นมาใหม่ตามปกติ
  • ถ้า Activity มีอยู่แล้ว และอยู่ข้างบนสุดของ Activity Stack – ระบบแอนดรอยด์จะไม่สร้างขึ้นมาใหม่ และส่งข้อมูลผ่าน onNewIntent แทน
  • ถ้า Activity มีอยู่แล้ว แต่ไม่ได้อยู่ข้างบนสุดของ Activity Stack – ระบบแอนดรอยด์จะเคลียร์ Activity ที่อยู่เหนือ Activity ตัวนั้นทั้งหมด เพื่อให้กลับมาอยู่ข้างบนสุดและทำงานต่อแทน  (และมีต่อในเนื้อหาถัดไป)
0:00
/

แต่จุดที่แตกต่างจาก android:launchMode="singleTop นิดหน่อย คือตอนที่ Activity มีอยู่ใน Activity Stack และถูกย้ายมาอยู่บนสุดเพื่อทำงานต่อ ลักษณะในการทำงานต่อจะขึ้นอยู่กับการกำหนด FLAG_ACTIVITY_CLEAR_TOP ด้วย โดยที่

  • ถ้ากำหนด FLAG_ACTIVITY_CLEAR_TOP – Activity ตัวนั้นจะทำงานต่อทันที และส่งข้อมูลผ่าน onNewIntent แทน
  • ถ้าไม่ได้กำหนด FLAG_ACTIVITY_CLEAR_TOP – ระบบแอนดรอยด์จะเคลียร์ Activity ตัวนั้นทิ้งแล้วสร้างขึ้นมาใหม่

ดังนั้นการกำหนด Flag ในรูปแบบนี้จะเหมาะกับ Activity ที่สามารถเปิดจากหน้าอื่นหรือเปิดจากตัวเองก็ได้ แต่ถ้าเปิดอยู่แล้วก็ให้ทำงานต่อจากใน Activity ตัวนั้นได้เลย

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

เพื่อป้องกันไม่ให้หน้า Checkout เปิดขึ้นมาซ้ำและกดปุ่ม Back เพื่อกลับไปหน้า Home ได้เลย การกำหนด Flag ด้วยวิธีแบบนี้จึงตอบโจทย์กว่า

FLAG_ACTIVITY_REORDER_TO_FRONT

Flag ตัวนี้จะมีผลในกรณีที่มี Activity อยู่แล้วใน Activity Stack โดยระบบแอนดรอยด์จะสลับ Activity ตัวนั้นให้ขึ้นมาอยู่ข้างบนสุดของ Activity Stack ทันที และจะไม่มีผลใด ๆ เมื่อใช้ร่วมกับ FLAG_ACTIVITY_CLEAR_TOP

0:00
/
ระวังผู้ใช้สับสน เพราะลำดับของ Activity Stack เปลี่ยนไป

FLAG_ACTIVITY_MULTIPLE_TASK + FLAG_ACTIVITY_NEW_DOCUMENT

Flag คู่นี้ออกแบบมาเพื่อแอปที่ต้องการการทำงานแบบ Multiple Task โดยระบบแอนดรอยด์จะสร้าง Task แยกสำหรับ Activity ตัวนั้นออกมาจาก Task เดิม

ดังนั้น Flag ชุดนี้จึงเหมาะกับการทำงานของแอปที่ต้องการเปิด Activity ที่มี Task แยกเป็นอิสระออกมาจาก Task หลักเพื่อทำงานบางอย่าง เช่น แอป File Explorer ที่สามารถกดเปิดไฟล์แยกหน้าต่างกันได้ และผู้ใช้สามารถสลับไปมาระหว่าง Task ผ่าน Recents Screen ได้ทันที

FLAG_ACTIVITY_LAUNCH_ADJACENT + FLAG_ACTIVITY_NEW_TASK

ใช้กับการแสดงผลแบบ Split-screen Mode ของ Multi-window เท่านั้น และไม่มีผลกับการแสดงผลแบบ Freeform Mode

ในกรณีที่ Task มี Activity ตัวนั้นอยู่ใน Activity Stack และอยู่บนหน้าต่างอีกฝั่งของ Split-screen Mode อยู่แล้ว ก็จะไม่เกิดอะไรขึ้น แต่ถ้าอยู่ในสถานะ Background ก็จะกลายเป็น Foreground และแสดงที่หน้าต่างอีกฝั่งของ Split-screen Mode ให้ทันที

0:00
/

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

สรุป

โดยปกติแล้วระบบแอนดรอยด์จะมีรูปแบบการทำงานของ Activity Stack ในลักษณะของ Last In, First Out ที่เรียงตามลำดับการสร้าง Activity ของระบบแอนดรอยด์ แต่ Launch Mode ในแต่ละแบบก็จะช่วยให้นักพัฒนาของแอปที่มีการทำงานที่ซับซ้อนและหลากหลาย สามารถกำหนดรูปแบบการแสดงของ Activity ได้ดั่งใจมากขึ้น

และ Launch Mode จะส่งผลต่อการทำงานของ Task และ Back Stack ของระบบแอนดรอยด์โดยตรง ดังนั้นนักพัฒนาก็จะต้องเข้าใจรูปแบบการทำงานเหล่านั้นด้วย เพื่อให้มั่นใจว่าแอปจะยังคงทำงานได้ถูกต้องตามต้องการ

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