Task Affinity ก็เปรียบเสมือนชื่อของ Task แต่ละตัว เพื่อให้ระบบแอนดรอยด์ใช้อ้างอิงเวลาสั่งให้ Task ทำงานใด ๆ ก็ตาม
บทความในชุดนี้
- ตอนที่ 1 - Introduction
- ตอนที่ 2 - Back Stack
- ตอนที่ 3 - Task
- ตอนที่ 4 - Home Screen และ Recents Screen
- ตอนที่ 5 - Activity Launch Mode [1/2]
- ตอนที่ 6 - Activity Launch Mode [2/2]
- ตอนที่ 7 - Task Affinity [Now Reading]
- ตอนที่ 8 - Multiple Task และ Concurrent Document
โดยปกติแล้วแอปแต่ละตัวจะมีแค่ Task เดียว และ Task นั้นก็จะมี Task Affinity เป็น Package Name ของแอป โดยที่นักพัฒนาไม่ต้องทำอะไร
แต่ถ้าต้องการกำหนด Task Affinity จะกำหนดผ่าน <activity>
ใน Android Manifest เท่านั้น
<!-- AndroidManifest.xml -->
<manifest ...>
<application ... >
<!-- ... -->
<activity
...
android:taskAffinity="any.task.affinity.name" />
</application>
</manifest>
Task Affinity จะมีผลก็ต่อเมื่อเข้าเงื่อนไขตามที่กำหนดเท่านั้น
การกำหนด Task Affinity ให้กับ Activity ใด ๆ จะมีผลก็ต่อเมื่อ
- Activity ตัวนั้นถูกเรียกผ่าน Intent ที่มีการกำหนด
FLAG_ACTIVITY_NEW_TASK
ไว้ - กำหนด
android:allowTaskReparenting="true"
ไว้ใน Activity ตัวนั้นนั่นหมายความว่าถ้าไม่เข้าทั้ง 2 เงื่อนไขนี้ การกำหนด Task Affinity จะไม่มีผลต่อการทำงานของ Task และระบบแอนดรอยด์ก็จะสร้าง Activity ดังกล่าวใน Task เดิมตามปกติ
Activity ถูกเรียกผ่าน Intent ที่กำหนด FLAG_ACTIVITY_NEW_TASK
ยกตัวอย่างเช่น เจ้าของบล็อกสร้าง ListActivity ที่กำหนด Task Affinity เป็น com.akexorcist.group1
แบบนี้
<!-- AndroidManifest.xml -->
<manifest ...>
<application ... >
<!-- ... -->
<activity
android:name=".ListActivity"
android:taskAffinity="com.akexorcist.group1" />
</application>
</manifest>
ถ้าเจ้าของบล็อกสร้าง Intent ที่ไม่มี FLAG_ACTIVITY_NEW_TASK
ก็จะเป็นการสร้าง ListActivity ใน Task เดิม แต่ถ้าสร้าง Intent และกำหนด FLAG_ACTIVITY_NEW_TASK
เพิ่มเข้าไปด้วย ระบบแอนดรอยด์ก็จะสร้าง Task ขึ้นมาใหม่โดยมี Task Affinity เป็น com.akexorcist.group1
ตามที่กำหนดไว้ใน Android Manifest นั่นเอง
ดังนั้น Activity จะถูกสร้างอยู่บน Task เดิมหรือ Task ตามที่กำหนด Task Affinity ไว้ ก็จะขึ้นอยู่กับว่ามี FLAG_ACTIVITY_NEW_TASK
หรือไม่
กำหนด android:allowTaskReparenting="true"
Attribute ตัวนี้จะใช้กับ Activity ที่สามารถเรียกใช้งานจากแอปอื่น ๆ ผ่าน Implicit Intent เท่านั้น
โดยปกติแล้วเวลาสร้าง Implicit Intent เพื่อเปิด Activity ในแอปอื่น ๆ สิ่งที่เกิดขึ้นคือระบบแอนดรอยด์จะสร้าง Activity ไว้ใน Task ของแอปที่เป็นคนเรียก
นั่นหมายความว่า Activity เหล่านี้สามารถอยู่บน Activity Stack ของแอปอื่น ๆ ได้ และจะอยู่จนกว่า Activity จะถูกทำลายทิ้ง
การกำหนด android:allowTaskReparenting="true"
ให้กับ Activity ดังกล่าวจะส่งผลทำให้ตอนที่แอปตัวนั้นถูกย่อและ Task กลายเป็น Background และเมื่อผู้ใช้กดเปิดผ่าน Home Screen ซึ่งจะทำให้ Task ที่เป็น Background เปลี่ยนเป็น Foreground ใหม่อีกครั้ง ระบบแอนดรอยด์จะย้าย Activity ตัวนั้นไปอยู่บน Task ของแอปตัวเองแทนที่จะอยู่ใน Task เดิมของแอปตัวอื่น และจะทำให้ Activity ตัวก่อนหน้าขึ้นมาแสดงบนหน้าจอแทน
การทำงานในลักษณะแบบนี้จะเหมาะกับบางสถานการณ์เท่านั้น เช่น แอปจำพวก Web Browser ที่ต้องการให้แอปตัวอื่น ๆ สามารถเปิด Activity สำหรับแสดงหน้าเว็ป และต้องการให้ Activity ถูกย้ายมาอยู่บน Task ของแอปตัวเอง (และจะมี Task Affinity ตามที่กำหนดไว้ใน android:taskAffinity
) เพื่อให้ผู้ใช้สามารถสลับแอปมาใช้งานต่อจากเดิมได้ เป็นต้น
แน่นอนว่าการทำงานในรูปแบบนี้มีแค่เพียงไม่กี่แอปเท่านั้น
รูปแบบการทำงานของ FLAG_ACTIVITY_NEW_TASK
อย่างที่บอกไปในตอนแรกว่าการกำหนด Task Affinity และกำหนด Flag ตัวนี้ลงใน Intent ระบบแอนดรอยด์จะสร้าง Activity ขึ้นมาใน Task ตัวใหม่ โดยมี Task Affinity ตามที่กำหนดไว้ ซึ่งนั่นเป็นแค่เพียงการทำงานในรูปแบบหนึ่งเท่านั้น
เพราะในความเป็นจริงนั้น การกำหนด FLAG_ACTIVITY_NEW_TASK
จะทำให้ระบบแอนดรอยด์เช็คก่อนว่าในแอปมี Task ไหนที่มี Activity ตัวนั้นเป็น Activity ตัวแรกหรือล่างสุด (Root Activity) ของ Activity Stack หรือไม่
- ถ้าไม่มี ระบบแอนดรอยด์ก็จะสร้าง Task สำหรับ Activity ตัวนั้นให้
- ถ้ามี ระบบแอนดรอยด์ก็จะสลับไปที่ Task นั้นแทน (ส่งผลให้ Task ก่อนหน้าเป็น Background และ Task ปลายทางเป็น Foreground)
การทำงานของFLAG_ACTIVITY_NEW_TASK
จะไม่ส่งผลต่อการเรียกonNewIntent
เพราะมาจากการทำงานของ Flag ตัวอื่น
และถึงแม้ว่า Task ดังกล่าวจะมี Activity อื่น ๆ อยู่ใน Activity Stack ด้วยก็ตาม ถ้า Root Activity ตรงตามเงื่อนไข ระบบแอนดรอยด์ก็จะสลับไปที่ Task นั้นให้อยู่ดี
ในขณะเดียวกัน Task ที่มี Activity ตัวนั้นอยู่ แต่ไม่ได้เป็น Root Activity ก็จะถือว่าไม่เข้าเงื่อนไข (และระบบแอนดรอยด์ก็จะสร้าง Task ใหม่แทน)
นอกจากนี้ Task Affinity กับ FLAG_ACTIVITY_NEW_TASK
จะไม่สามารถใช้กับ startActivityForResult
เพราะ onActivityResult
จะถูกเรียกทันทีและได้ Result เป็น Activity.RESULT_CANCELED
สรุป
ถ้าลองย้อนกลับไปดูบทความก่อนหน้า ก็จะเห็นว่าการใช้ FLAG_ACTIVITY_NEW_TASK
ควบคู่กับ Flag ตัวอื่น ๆ ก็จะขึ้นอยู่กับการกำหนด Task Affinity ใน <activity>
ด้วย
ดังนั้นถ้านักพัฒนาเข้าใจการทำงานของ Task Affinity ก็จะช่วยให้เราสามารถคาดเดาการทำงานที่จะเกิดขึ้นในแต่ละสถานการณ์ได้อย่างครอบคลุมนั่นเอง