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

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

โดยปกติแล้วแอปแต่ละตัวจะมีแค่ 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 ก็จะช่วยให้เราสามารถคาดเดาการทำงานที่จะเกิดขึ้นในแต่ละสถานการณ์ได้อย่างครอบคลุมนั่นเอง

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