Task และ Back Stack ตอนที่ 6 - Activity Launch Mode [2/2]
สำหรับบทความนี้ไม่ขอเกริ่นอะไรมาก เพราะเป็นบทความที่ต่อเนื่องมาจากบทความก่อนหน้า ที่อธิบายเรื่องราวของ Activity Launch Mode โดยเฉพาะ
เจ้าของบล็อกขอเรียก Activity Launch Mode แบบสั้น ๆ ว่า Launch Mode
บทความในชุดนี้
- ตอนที่ 1 - Introduction
- ตอนที่ 2 - Back Stack
- ตอนที่ 3 - Task
- ตอนที่ 4 - Home Screen และ Recents Screen
- ตอนที่ 5 - Activity Launch Mode [1/2]
- ตอนที่ 6 - Activity Launch Mode [2/2] [Now Reading]
- ตอนที่ 7 - Task Affinity
- ตอนที่ 8 - Multiple Task และ Concurrent Document
จากความเดิมในตอนที่แล้ว
ในบทความก่อนหน้านี้ เจ้าของบล็อกก็ได้พูดถึงหน้าที่ของ 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 ตัวใหม่
จึงทำให้การใช้ 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 ตัวนั้นทั้งหมด เพื่อให้กลับมาอยู่ข้างบนสุดและทำงานต่อแทน (และมีต่อในเนื้อหาถัดไป)
แต่จุดที่แตกต่างจาก 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
ระวังผู้ใช้สับสน เพราะลำดับของ 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 ให้ทันที
และถ้าเพิ่ม FLAG_ACTIVITY_MULTIPLE_TASK
เข้าไปด้วย จะทำให้ระบบแอนดรอยด์สร้าง Task ขึ้นมาใหม่ให้ทุกครั้งแทน โดยไม่สนใจว่าจะมี Task เก่าที่มี Activity ตัวนั้นอยู่หรือไม่
สรุป
โดยปกติแล้วระบบแอนดรอยด์จะมีรูปแบบการทำงานของ Activity Stack ในลักษณะของ Last In, First Out ที่เรียงตามลำดับการสร้าง Activity ของระบบแอนดรอยด์ แต่ Launch Mode ในแต่ละแบบก็จะช่วยให้นักพัฒนาของแอปที่มีการทำงานที่ซับซ้อนและหลากหลาย สามารถกำหนดรูปแบบการแสดงของ Activity ได้ดั่งใจมากขึ้น
และ Launch Mode จะส่งผลต่อการทำงานของ Task และ Back Stack ของระบบแอนดรอยด์โดยตรง ดังนั้นนักพัฒนาก็จะต้องเข้าใจรูปแบบการทำงานเหล่านั้นด้วย เพื่อให้มั่นใจว่าแอปจะยังคงทำงานได้ถูกต้องตามต้องการ