Intent และตัวกรอง Intent

Intent เป็นออบเจ็กต์การรับส่งข้อความที่คุณใช้เพื่อขอการดำเนินการได้ จากคอมโพเนนต์ของแอปอื่น แม้ว่า Intent จะอำนวยความสะดวกในการสื่อสารระหว่างองค์ประกอบต่างๆ หลายวิธี แต่มีสามวิธี กรณีการใช้งานพื้นฐาน

  • การเริ่มกิจกรรม

    Activity หมายถึงหน้าจอเดียวในแอป คุณสามารถเริ่ม ของ Activity โดยการส่ง Intent ไปยัง startActivity() Intent อธิบายกิจกรรมที่จะเริ่มต้นและเก็บข้อมูลที่จำเป็น

    หากคุณต้องการรับผลลัพธ์จากกิจกรรมเมื่อกิจกรรมเสร็จสิ้นแล้ว โทรหา startActivityForResult() กิจกรรมจะได้รับผลลัพธ์ เป็นออบเจ็กต์ Intent แยกต่างหากใน Callback onActivityResult() ของกิจกรรม ดูข้อมูลเพิ่มเติมได้ในคู่มือกิจกรรม

  • การเริ่มต้นบริการ

    Service เป็นคอมโพเนนต์ที่ทำงานอยู่เบื้องหลัง โดยไม่ต้องมีอินเทอร์เฟซผู้ใช้ สำหรับ Android 5.0 (API ระดับ 21) ขึ้นไป คุณจะเริ่มใช้บริการได้ ด้วย JobScheduler หากต้องการดูข้อมูลเพิ่มเติม เกี่ยวกับ JobScheduler โปรดดู API-reference documentation

    สำหรับเวอร์ชันก่อน Android 5.0 (API ระดับ 21) คุณเริ่มบริการได้โดยใช้ ของคลาส Service คุณเริ่มใช้บริการได้ เพื่อดำเนินการแบบครั้งเดียว (เช่น การดาวน์โหลดไฟล์) โดยการส่ง Intent ไปยัง startService() Intent อธิบายบริการที่จะเริ่มต้นและดำเนินการข้อมูลที่จำเป็น

    หากบริการออกแบบมาโดยใช้อินเทอร์เฟซแบบไคลเอ็นต์-เซิร์ฟเวอร์ คุณสามารถเชื่อมโยงกับบริการได้ จากคอมโพเนนต์อื่นโดยการส่ง Intent ไปยัง bindService() ดูข้อมูลเพิ่มเติมได้ในคู่มือบริการ

  • การส่งการออกอากาศ

    การประกาศคือข้อความที่แอปใดก็ตามสามารถรับได้ ระบบจะแสดง การประกาศสำหรับเหตุการณ์ของระบบ เช่น เมื่อระบบเปิดเครื่องขึ้นหรืออุปกรณ์เริ่มชาร์จ คุณส่งประกาศไปยังแอปอื่นๆ ได้โดยการส่ง Intent เป็น sendBroadcast() หรือ sendOrderedBroadcast()

ส่วนที่เหลือของหน้านี้จะอธิบายวิธีการทำงานของ Intent และวิธีใช้ สำหรับข้อมูลที่เกี่ยวข้อง โปรดดู การโต้ตอบกับแอปอื่นๆ และการแชร์เนื้อหา

ประเภทความตั้งใจ

Intent มี 2 ประเภท ได้แก่

  • Intent แบบเจาะจงปลายทางระบุว่าคอมโพเนนต์ใดของแอปพลิเคชันที่จะตอบสนองต่อ Intent นั้น โดยระบุ ComponentName แบบเต็ม คุณจะ ซึ่งมักใช้ Intent ที่ชัดเจน เพื่อเริ่มคอมโพเนนต์ใน แอปของคุณเอง เนื่องจากทราบชื่อชั้นเรียนของกิจกรรมหรือบริการที่คุณต้องการเริ่ม สำหรับ ตัวอย่างเช่น คุณอาจเริ่มกิจกรรมใหม่ภายในแอปเพื่อตอบสนองต่อการดำเนินการของผู้ใช้ หรือเริ่มต้น บริการเพื่อดาวน์โหลดไฟล์ในเบื้องหลัง
  • Intent แบบไม่เจาะจงปลายทางจะไม่ตั้งชื่อคอมโพเนนต์ที่เฉพาะเจาะจง แต่จะประกาศการดำเนินการทั่วไปแทน ซึ่งทำให้คอมโพเนนต์จากแอปอื่นสามารถจัดการได้ ตัวอย่างเช่น หากคุณต้องการ แสดงตำแหน่งบนแผนที่แก่ผู้ใช้ คุณสามารถใช้ Intent แบบไม่เจาะจงปลายทางเพื่อขอ แสดงตำแหน่งที่ระบุในแผนที่

รูปที่ 1 แสดงวิธีการใช้ Intent เมื่อเริ่มกิจกรรม เมื่อ ออบเจ็กต์ Intent ตั้งชื่อคอมโพเนนต์กิจกรรมที่เจาะจงอย่างชัดเจน ระบบ คอมโพเนนต์ดังกล่าวจะเริ่มทำงานทันที

รูปที่ 1 เจตนาโดยนัย ที่ส่งผ่านระบบเพื่อเริ่มกิจกรรมอื่น: [1] กิจกรรม A จะสร้าง Intent พร้อมคำอธิบายการดำเนินการและส่งไปยัง startActivity() [2] ระบบ Android ค้นหาทั้งหมด แอปสำหรับตัวกรอง Intent ที่ตรงกับ Intent เมื่อพบการจับคู่ที่ตรงกัน [3] ระบบ เริ่มกิจกรรมการจับคู่ (กิจกรรม B) โดยการเรียกใช้เมธอด onCreate() และส่งผ่าน Intent

เมื่อคุณใช้ Intent แบบไม่เจาะจงปลายทาง ระบบ Android จะค้นหาคอมโพเนนต์ที่เหมาะสมเพื่อเริ่ม โดยเปรียบเทียบเนื้อหาของ Intent กับตัวกรองความตั้งใจที่ประกาศไว้ในไฟล์ Manifest ของแอปอื่นๆ บน อุปกรณ์ หาก Intent ตรงกับตัวกรอง Intent ระบบจะเริ่มคอมโพเนนต์นั้นและแสดงโฆษณา ออบเจ็กต์ Intent หากตัวกรอง Intent หลายรายการใช้งานร่วมกันได้ ระบบจะ แสดงกล่องโต้ตอบเพื่อให้ผู้ใช้เลือกแอปที่จะใช้

ตัวกรอง Intent คือนิพจน์ในไฟล์ Manifest ของแอปที่ ระบุประเภทของ Intent ที่คอมโพเนนต์ ต้องการได้รับ เช่น ด้วยการประกาศตัวกรอง Intent สำหรับกิจกรรม ทำให้แอปอื่นๆ สามารถเริ่มกิจกรรมได้โดยตรงด้วยความตั้งใจบางอย่าง ในทำนองเดียวกัน หากคุณไม่ได้ประกาศตัวกรอง Intent สำหรับกิจกรรม ตัวกรองดังกล่าวก็เริ่มต้นใช้งานได้ ด้วยเจตนาที่ชัดแจ้งเท่านั้น

ข้อควรระวัง: เพื่อดูแลให้แอปปลอดภัย ให้ดำเนินการดังนี้ ใช้ภาษาที่ไม่เหมาะสม Intent เมื่อเริ่มต้น Service และไม่ ประกาศตัวกรอง Intent สำหรับบริการของคุณ การใช้ Intent แบบไม่เจาะจงปลายทางเพื่อเริ่มบริการเป็น เนื่องจากคุณไม่สามารถแน่ใจได้ว่าบริการใดจะตอบสนองต่อจุดประสงค์นั้น และผู้ใช้ไม่สามารถเห็นได้ว่าบริการใดเริ่มทำงานบ้าง ใน Android 5.0 (API ระดับ 21) ระบบจะเลือกใช้ จะมีข้อยกเว้นหากคุณโทรหา bindService() โดยมีเจตนาโดยนัย

การสร้างความตั้งใจ

ออบเจ็กต์ Intent มีข้อมูลที่ระบบ Android ใช้ กำหนดคอมโพเนนต์ที่จะเริ่ม (เช่น ชื่อคอมโพเนนต์หรือคอมโพเนนต์ที่ตรงกันทั้งหมด ที่ควรได้รับความตั้งใจ) รวมถึงข้อมูลที่คอมโพเนนต์ผู้รับใช้ เพื่อดำเนินการอย่างเหมาะสม (เช่น การดำเนินการที่ต้องทำและข้อมูลในการดำเนินการ)

ข้อมูลหลักที่อยู่ใน Intent คือ

ชื่อคอมโพเนนต์
ชื่อของคอมโพเนนต์ที่จะเริ่มต้น

ขั้นตอนนี้เป็นขั้นตอนที่ไม่บังคับ แต่เป็นข้อมูลสำคัญที่ช่วยสร้างความตั้งใจ Explicit ซึ่งหมายความว่าควรแสดง Intent ไปยังคอมโพเนนต์ของแอปเท่านั้น กำหนดโดยชื่อคอมโพเนนต์ หากไม่มีชื่อคอมโพเนนต์ Intent จะเป็น implicit และ ระบบจะพิจารณาว่าคอมโพเนนต์ใดควรได้รับ Intent โดยพิจารณาจากข้อมูล Intent อื่นๆ (เช่น การกระทำ ข้อมูล และหมวดหมู่ตามที่อธิบายไว้ด้านล่าง) หากคุณต้องการเริ่มต้น คอมโพเนนต์ในแอป คุณควรระบุชื่อคอมโพเนนต์

หมายเหตุ: เมื่อเริ่ม Service ระบุชื่อคอมโพเนนต์เสมอ มิฉะนั้น คุณไม่สามารถ แน่ใจ ได้ว่า บริการใด จะตอบสนองต่อความตั้งใจ และผู้ใช้ไม่สามารถเห็นได้ว่าบริการใดเริ่มทำงานใด

ช่องนี้ของ Intent คือ ComponentName ซึ่งคุณระบุได้โดยใช้ ชื่อคลาสที่มีคุณสมบัติตามเกณฑ์ของคอมโพเนนต์เป้าหมาย รวมถึงชื่อแพ็กเกจของแอป ตัวอย่างเช่น com.example.ExampleActivity คุณตั้งชื่อคอมโพเนนต์ได้ด้วย setComponent(), setClass(), setClassName(), หรือด้วยเมธอด เครื่องมือสร้าง Intent

การดำเนินการ
สตริงที่ระบุการกระทำทั่วไปที่จะดำเนินการ (เช่น view หรือ pick)

ในกรณีของความตั้งใจในการออกอากาศ นี่คือการดำเนินการที่เกิดขึ้นและมีการรายงาน การดำเนินการจะเป็นตัวกำหนดโครงสร้างของความตั้งใจที่เหลือเป็นหลัก โดยเฉพาะอย่างยิ่ง ข้อมูลที่อยู่ในข้อมูลและอื่นๆ

คุณสามารถระบุการทำงานของคุณเองเพื่อใช้โดย Intent ภายในแอป (หรือสำหรับการใช้งานโดยบุคคลอื่น แอปเพื่อเรียกคอมโพเนนต์ในแอป) แต่โดยปกติแล้ว คุณมักจะระบุค่าคงที่การดำเนินการ กำหนดโดยคลาส Intent หรือคลาสเฟรมเวิร์กอื่นๆ นี่คือบางส่วน การทำงานทั่วไปสำหรับการเริ่มกิจกรรม

ACTION_VIEW
ใช้การดำเนินการนี้ใน Intent กับ startActivity() เมื่อคุณมีข้อมูลบางอย่างที่ กิจกรรมสามารถแสดงต่อผู้ใช้ เช่น รูปภาพที่จะดูในแอปแกลเลอรี หรือที่อยู่ที่จะ ดูในแอปแผนที่
ACTION_SEND
หรือเรียกอีกอย่างว่า Intent แชร์ คุณควรใช้ Intent นี้กับ startActivity() เมื่อคุณมีข้อมูลบางอย่างที่ผู้ใช้สามารถทำได้ แชร์ผ่านแอปอื่น เช่น แอปอีเมลหรือแอปการแชร์ผ่านโซเชียล

ดูข้อมูลเพิ่มเติมในข้อมูลอ้างอิงของชั้นเรียนสำหรับ Intent ค่าคงที่ที่กำหนดการกระทำทั่วไป มีการกำหนดการดำเนินการอื่นๆ ที่อื่นๆ ในเฟรมเวิร์ก Android เช่น ใน Settings สำหรับการดำเนินการ ที่เปิดหน้าจอที่เจาะจงในแอปการตั้งค่าของระบบ

คุณระบุการดำเนินการสำหรับ Intent ได้ด้วย setAction() หรือด้วยตัวสร้าง Intent

หากคุณกำหนดการดำเนินการของคุณเอง อย่าลืมใส่ชื่อแพ็กเกจของแอปด้วย นำหน้า ดังที่ปรากฏในตัวอย่างต่อไปนี้

Kotlin

const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
ข้อมูล
URL (ออบเจ็กต์ Uri) ที่อ้างอิงข้อมูล ดำเนินการและ/หรือ ประเภท MIME ของข้อมูลนั้น โดยทั่วไปประเภทของข้อมูลที่ระบุจะกำหนดโดยการดำเนินการของ Intent สำหรับ เช่น หากการดำเนินการคือ ACTION_EDIT ข้อมูลควรมี URI ของเอกสารที่จะแก้ไข

เมื่อสร้าง Intent นอกจาก URI แล้ว ยังจำเป็นต้องระบุประเภทข้อมูล (ประเภท MIME) ด้วย ตัวอย่างเช่น กิจกรรมที่สามารถแสดงภาพได้คงไม่สามารถ เพื่อเล่นไฟล์เสียง แม้ว่ารูปแบบ URI อาจคล้ายกันก็ตาม การระบุประเภท MIME ของข้อมูลจะช่วยให้ Android ระบบจะค้นหาองค์ประกอบที่ดีที่สุดเพื่อให้ได้รับความตั้งใจของคุณ อย่างไรก็ตาม บางครั้งประเภท MIME สามารถอนุมานได้จาก URI โดยเฉพาะอย่างยิ่งเมื่อข้อมูลเป็น URI content: URI content: บ่งบอกว่าข้อมูลอยู่ในอุปกรณ์ และควบคุมโดย ContentProvider ซึ่งทำให้ระบบมองเห็นประเภท MIME ของข้อมูล

หากต้องการตั้งค่าเฉพาะ URI ข้อมูล ให้เรียก setData() หากต้องการตั้งค่าเฉพาะประเภท MIME ให้เรียกใช้ setType() หากจำเป็น คุณ สามารถตั้งค่าทั้ง 2 แบบอย่างชัดแจ้งด้วย setDataAndType()

ข้อควรระวัง: หากต้องการตั้งค่าทั้งประเภท URI และ MIME ไม่ต้องโทรหา setData() และ setType() เนื่องจากแต่ละค่าจะทำให้มูลค่าของกันและกันเป็นโมฆะ ใช้ setDataAndType() เสมอเพื่อตั้งค่าทั้ง 2 แบบ URI และ ประเภท MIME

หมวดหมู่
สตริงที่มีข้อมูลเพิ่มเติมเกี่ยวกับประเภทของคอมโพเนนต์ ที่ควรจัดการกับความตั้งใจ คำอธิบายหมวดหมู่กี่รายการก็ได้ อยู่ใน Intent แต่ Intent ส่วนใหญ่ไม่จำเป็นต้องมีหมวดหมู่ ต่อไปนี้คือหมวดหมู่ทั่วไปบางส่วน
CATEGORY_BROWSABLE
กิจกรรมเป้าหมายอนุญาตให้เว็บเบราว์เซอร์เริ่มดำเนินการเองเพื่อแสดงข้อมูล อ้างอิงโดยลิงก์ เช่น รูปภาพหรือข้อความอีเมล
CATEGORY_LAUNCHER
กิจกรรมเป็นกิจกรรมเริ่มต้นของงานและแสดงอยู่ใน Launcher ของแอปพลิเคชันของระบบ

ดูรายการทั้งหมดของคำอธิบายชั้นเรียน Intent หมวดหมู่ต่างๆ

คุณระบุหมวดหมู่ได้ด้วย addCategory()

พร็อพเพอร์ตี้ที่แสดงด้านบน (ชื่อคอมโพเนนต์ การทำงาน ข้อมูล และหมวดหมู่) แสดง ซึ่งเป็นการกำหนดลักษณะของเจตนา เมื่ออ่านคุณสมบัติเหล่านี้ ระบบ Android กำหนดคอมโพเนนต์ของแอปที่ควรเริ่มทำงานได้ อย่างไรก็ตาม ความตั้งใจสามารถ ข้อมูลเพิ่มเติมที่ไม่ส่งผลกระทบต่อ วิธีแก้ไขในคอมโพเนนต์ของแอป Intent ยังจะให้ข้อมูลต่อไปนี้ได้ด้วย

เพิ่มเติม
คู่คีย์-ค่าที่มีข้อมูลเพิ่มเติมที่จำเป็นในการบรรลุ การทำงานที่ขอ เช่นเดียวกับการทำงานบางอย่างใช้ URI ข้อมูลบางประเภท การทำงานบางอย่างจะใช้ส่วนเพิ่มเติมบางอย่างด้วย

คุณเพิ่มข้อมูลเพิ่มเติมได้ด้วยวิธีการ putExtra() หลายวิธี แต่ละรายการยอมรับพารามิเตอร์ 2 ตัว ได้แก่ ชื่อคีย์และค่า คุณยังสร้างออบเจ็กต์ Bundle ที่มีข้อมูลเพิ่มเติมทั้งหมดจากนั้นแทรก Bundle ใน Intent ด้วย putExtras()

ตัวอย่างเช่น เมื่อสร้างความตั้งใจที่จะส่งอีเมลด้วย ACTION_SEND คุณสามารถระบุผู้รับถึงด้วยแอตทริบิวต์ EXTRA_EMAIL และระบุหัวเรื่องด้วยแอตทริบิวต์ คีย์ EXTRA_SUBJECT

คลาส Intent ระบุค่าคงที่ EXTRA_* จำนวนมาก สำหรับประเภทข้อมูลมาตรฐาน หากจำเป็นต้องประกาศคีย์เพิ่มเติมของคุณเอง (สำหรับ Intent ที่ ที่แอปของคุณได้รับ) อย่าลืมระบุชื่อแพ็กเกจของแอป นำหน้า ดังที่ปรากฏในตัวอย่างต่อไปนี้

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

ข้อควรระวัง: อย่าใช้ Parcelable หรือ Serializable เมื่อส่ง Intent ที่คุณคาดหวัง อีกแอปหนึ่งที่จะรับ หากแอป พยายามเข้าถึงข้อมูลในออบเจ็กต์ Bundle แต่ไม่ ที่มีสิทธิ์เข้าถึงคลาสที่เรียงเป็นแถวหรือเรียงต่อกัน ระบบจะนำเสนอ RuntimeException

การแจ้งว่าไม่เหมาะสม
แฟล็กได้รับการกำหนดไว้ในคลาส Intent ซึ่งทำหน้าที่เป็นข้อมูลเมตาสำหรับ Intent การรายงานปัญหาอาจสั่งให้ระบบ Android เปิดใช้งานกิจกรรม (ตัวอย่างเช่น งาน กิจกรรมควรเป็น กับ) และวิธีจัดการกับผลิตภัณฑ์หลังเปิดตัว (เช่น ระบุว่าอยู่ในรายการล่าสุดของ กิจกรรม)

สำหรับข้อมูลเพิ่มเติม โปรดดูเมธอด setFlags()

ตัวอย่าง Intent แบบเจาะจง

Intent แบบเจาะจงคือสิ่งที่คุณใช้เพื่อเปิดคอมโพเนนต์แอปที่เจาะจง เช่น กิจกรรมหรือบริการหนึ่งๆ ในแอป หากต้องการสร้าง Intent ที่ชัดเจน ให้นิยาม ชื่อคอมโพเนนต์สำหรับออบเจ็กต์ Intent - จะใช้พร็อพเพอร์ตี้ Intent อื่นๆ หรือไม่ก็ได้

ตัวอย่างเช่น หากคุณสร้างบริการในแอปโดยมีชื่อว่า DownloadService ที่ออกแบบมาเพื่อดาวน์โหลดไฟล์จากเว็บ คุณสามารถเริ่มใช้งานด้วยโค้ดต่อไปนี้

Kotlin

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)

Java

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

Intent(Context, Class) เครื่องมือสร้างแอปจะให้แอป Context และ ให้ออบเจ็กต์ Class ด้วยเหตุนี้ Intent นี้จะเริ่มต้นคลาส DownloadService ในแอปอย่างชัดเจน

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการสร้างและเริ่มต้นบริการ โปรดดูที่ คู่มือบริการ

ตัวอย่าง Intent แบบไม่เจาะจงปลายทาง

Intent แบบไม่เจาะจงปลายทางจะระบุการดำเนินการที่สามารถเรียกใช้แอปในอุปกรณ์ที่สามารถเรียกใช้ เพื่อดำเนินการ การใช้ Intent แบบไม่เจาะจงปลายทางมีประโยชน์เมื่อแอปดำเนินการต่อไปนี้ไม่ได้ แต่แอปอื่นๆ สามารถทำได้ และคุณต้องการให้ผู้ใช้เลือกแอปที่จะใช้

เช่น หากคุณมีเนื้อหาที่อยากให้ผู้ใช้ แชร์กับผู้อื่น สร้าง Intent ด้วยการดำเนินการ ACTION_SEND และเพิ่มเนื้อหาพิเศษที่ระบุเนื้อหาที่จะแชร์ เมื่อคุณโทร startActivity()ด้วยความตั้งใจดังกล่าว ผู้ใช้จึงสามารถ เลือกแอปที่จะแชร์เนื้อหา

Kotlin

// Create the text message with a string.
val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, textMessage)
    type = "text/plain"
}

// Try to invoke the intent.
try {
    startActivity(sendIntent)
} catch (e: ActivityNotFoundException) {
    // Define what your app should do if no activity can handle the intent.
}

Java

// Create the text message with a string.
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Try to invoke the intent.
try {
    startActivity(sendIntent);
} catch (ActivityNotFoundException e) {
    // Define what your app should do if no activity can handle the intent.
}

เมื่อมีการเรียก startActivity() ระบบจะ ตรวจสอบแอปที่ติดตั้งไว้ทั้งหมด เพื่อดูว่าแอปใด ที่รับมือกับความตั้งใจประเภทนี้ได้ ( Intent ที่มีการดำเนินการ ACTION_SEND ที่มี "ข้อความ/ธรรมดา" ข้อมูล) หากมีเพียงแอปเดียวที่จัดการได้ แอปนั้นจะเปิดขึ้นทันทีและ Intent หากไม่มีแอปอื่นๆ ที่สามารถจัดการได้ แอปของคุณจะสามารถตรวจจับ ActivityNotFoundException ที่เกิดขึ้น หากมีหลายกิจกรรมยอมรับ Intent แสดงกล่องโต้ตอบ เช่น กล่องโต้ตอบที่แสดงในรูปที่ 2 เพื่อให้ผู้ใช้เลือกได้ว่าจะใช้แอปใด

ข้อมูลเพิ่มเติมเกี่ยวกับการเปิดตัวแอปอื่นๆ มีอยู่ในคำแนะนำแล้ว เกี่ยวกับการส่งผู้ใช้ไปยัง แอปอื่น

รูปที่ 2 กล่องโต้ตอบตัวเลือก

การบังคับตัวเลือกแอป

เมื่อมีแอปมากกว่า 1 แอปที่ตอบสนองต่อความตั้งใจโดยนัยของคุณ ผู้ใช้สามารถเลือกแอปที่จะใช้ และทำให้แอปนั้นเป็นตัวเลือกเริ่มต้นสำหรับ การดำเนินการ ความสามารถในการเลือกค่าเริ่มต้นจะเป็นประโยชน์ในการดำเนินการที่ผู้ใช้ อาจต้องการใช้แอปเดิมทุกครั้ง เช่น เมื่อเปิดหน้าเว็บ (ผู้ใช้ มักจะใช้เว็บเบราว์เซอร์เพียงรายการเดียว)

แต่ถ้าแอปหลายแอปสามารถตอบสนองต่อความตั้งใจได้ และผู้ใช้อาจต้องการใช้แอปอื่น ทุกครั้ง คุณควรแสดงกล่องโต้ตอบตัวเลือกอย่างชัดเจน กล่องโต้ตอบตัวเลือกจะถามว่า ให้ผู้ใช้เลือกแอปที่จะใช้ในการทำงาน (ผู้ใช้ไม่สามารถเลือกแอปเริ่มต้นให้ การดำเนินการดังกล่าว) เช่น เมื่อแอปของคุณทำการ "แชร์" เมื่อมีการดําเนินการ ACTION_SEND ผู้ใช้อาจต้องการแชร์โดยใช้แอปอื่น ซึ่งขึ้นอยู่กับ ในสถานการณ์ปัจจุบัน ดังนั้นคุณควรใช้กล่องโต้ตอบตัวเลือกเสมอ ดังแสดงในรูปที่ 2

หากต้องการแสดงตัวเลือก ให้สร้าง Intent โดยใช้ createChooser() แล้วส่งไปยัง startActivity() ดังที่ปรากฏในตัวอย่างต่อไปนี้ ตัวอย่างนี้แสดงกล่องโต้ตอบที่มีรายการแอปที่ตอบสนองต่อ Intent ที่ส่งไปยังเมธอด createChooser() และใช้ข้อความที่ระบุเป็น ชื่อกล่องโต้ตอบ

Kotlin

val sendIntent = Intent(Intent.ACTION_SEND)
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
val title: String = resources.getString(R.string.chooser_title)
// Create intent to show the chooser dialog
val chooser: Intent = Intent.createChooser(sendIntent, title)

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(chooser)
}

Java

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

ตรวจจับการเปิดตัว Intent ที่ไม่ปลอดภัย

แอปของคุณอาจเปิดตัว Intent เพื่อสลับไปมาระหว่างคอมโพเนนต์ต่างๆ ภายในแอป หรือดำเนินการในนามของแอปอื่น เพื่อปรับปรุงความปลอดภัยของแพลตฟอร์ม Android 12 (API ระดับ 31) ขึ้นไปมีฟีเจอร์การแก้ไขข้อบกพร่องที่จะเตือนคุณ หากแอปเรียกใช้ Intent ที่ไม่ปลอดภัย ตัวอย่างเช่น แอปของคุณอาจ เรียกใช้Intent ที่ซ้อนกัน ซึ่งเป็น Intent ที่ส่งอย่างไม่ปลอดภัย เพื่อเป็นส่วนเสริมในจุดประสงค์อื่น

หากแอปทำทั้ง 2 อย่างต่อไปนี้ ระบบจะตรวจหาที่ไม่ปลอดภัย การเปิดตัว Intent และการละเมิด StrictMode เกิดขึ้น:

  1. แอปของคุณยกเลิกการแปลง Intent ที่ซ้อนกันจาก Intent เพิ่มเติม
  2. แอปของคุณจะเริ่มแอปทันที โดยใช้ Intent ซ้อนนั้น เช่น การส่งผ่านความตั้งใจไปยัง startActivity() startService(), หรือ bindService()

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีระบุสถานการณ์นี้และทำการเปลี่ยนแปลงแอป อ่านบล็อกโพสต์เกี่ยวกับการวางอุปกรณ์ Android Intent ใน Medium

ตรวจสอบการเปิดตัว Intent ที่ไม่ปลอดภัย

หากต้องการตรวจสอบการเปิด Intent ที่ไม่ปลอดภัยในแอป โปรดเรียกใช้ detectUnsafeIntentLaunch() เมื่อกำหนดค่า VmPolicy ดังที่แสดงในข้อมูลโค้ดต่อไปนี้ ถ้า แอปของคุณตรวจพบการละเมิด StrictMode คุณอาจต้องหยุดการดำเนินการกับแอปเพื่อ ปกป้องข้อมูลที่อาจมีความละเอียดอ่อน

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build())
}

Java

protected void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build());
}

ใช้ Intent อย่างมีความรับผิดชอบมากขึ้น

หากต้องการลดโอกาสในการเปิดตัว Intent ที่ไม่ปลอดภัยและการละเมิด StrictMode ให้ทำตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้

คัดลอกเฉพาะส่วนเพิ่มเติมที่จำเป็นภายใน Intent และดำเนินการตามความจำเป็น ด้านสุขอนามัยและการตรวจสอบความถูกต้อง แอปอาจคัดลอกบริการเพิ่มเติมจาก Intent หนึ่งไปยัง อีก Intent หนึ่งที่ใช้เพื่อเปิดตัวคอมโพเนนต์ใหม่ กรณีนี้จะเกิดขึ้นเมื่อ การโทรในแอป putExtras(Intent) หรือ putExtras(Bundle) หากแอปทำอย่างใดอย่างหนึ่งต่อไปนี้ ให้คัดลอกเฉพาะส่วนเพิ่มเติมที่ ต้องการคอมโพเนนต์ที่รับ หากเจตนาอื่นๆ (ที่ได้รับสำเนา) เปิดใช้คอมโพเนนต์ที่ไม่ใช่ ส่งออกแล้ว ทำความสะอาดข้อมูล และ ตรวจสอบความถูกต้องของส่วนเพิ่มเติมก่อนที่จะคัดลอกโดยมีเจตนาที่จะเปิดตัว คอมโพเนนต์

อย่าส่งออกคอมโพเนนต์ของแอปโดยไม่จำเป็น ตัวอย่างเช่น หากคุณ ต้องการเปิดใช้งานคอมโพเนนต์แอปโดยใช้ Intent ที่ฝังไว้ภายใน ให้ตั้งค่า แอตทริบิวต์ android:exported ของคอมโพเนนต์เป็น false

ใช้ PendingIntent แทน Intent ซ้อนกัน ด้วยวิธีนี้ เมื่อแอปอื่นยกเลิกการแปลง PendingIntent ที่มี Intent แอปอื่นสามารถเปิด PendingIntent โดยใช้ เฉพาะตัวของแอปคุณ การกำหนดค่านี้ช่วยให้แอปอื่นเปิดใช้งานได้อย่างปลอดภัย คอมโพเนนต์ใดๆ ก็ตามในแอป ซึ่งรวมถึงคอมโพเนนต์ที่ไม่ได้ส่งออก

แผนภาพที่ 2 แสดงให้เห็นว่าระบบส่งการควบคุมจาก (ไคลเอ็นต์) อย่างไร ไปยังแอป (บริการ) อื่น แล้วกลับไปยังแอปของคุณ

  1. แอปของคุณสร้าง Intent ที่เรียกใช้กิจกรรมในแอปอื่น ภายใน เพื่อเพิ่มออบเจ็กต์ PendingIntent เป็นส่วนเสริม Intent ที่รอดำเนินการนี้ เรียกใช้คอมโพเนนต์ในแอปของคุณ ระบบจะไม่ส่งออกคอมโพเนนต์นี้
  2. เมื่อได้รับ Intent ของแอปแล้ว แอปอื่นๆ จะดึงข้อมูลที่ซ้อนกัน ออบเจ็กต์ PendingIntent รายการ
  3. แอปอื่นเรียกใช้เมธอด send() ในออบเจ็กต์ PendingIntent
  4. เมื่อส่งการควบคุมกลับไปยังแอปของคุณแล้ว ระบบจะเรียกใช้การตั้งค่าที่รอดำเนินการ โดยใช้บริบทของแอป

รูปที่ 2 แผนภาพการสื่อสารระหว่างแอปเมื่อใช้รายการที่ฝังซึ่งรอดําเนินการ Intent

ได้รับความตั้งใจโดยนัย

หากต้องการโฆษณา Intent แบบไม่เจาะจงปลายทางที่แอปของคุณรับได้ ให้ประกาศตัวกรอง Intent อย่างน้อย 1 รายการสำหรับ คอมโพเนนต์ของแอปแต่ละรายการด้วย <intent-filter> ในไฟล์ Manifest ของคุณ ตัวกรอง Intent แต่ละรายการจะระบุประเภทของ Intent ที่ ตัวกรองยอมรับ โดยอิงตามการดำเนินการของ Intent ข้อมูล และหมวดหมู่ ระบบจะส่ง Intent แบบไม่เจาะจงปลายทางไปยังคอมโพเนนต์แอปเฉพาะในกรณีที่ Intent ส่งผ่านตัวกรอง Intent ตัวใดตัวหนึ่งได้

หมายเหตุ: ระบบจะส่ง Intent แบบเจาะจงปลายทางเสมอ โดยไม่คำนึงถึงตัวกรอง Intent ที่คอมโพเนนต์ประกาศ

คอมโพเนนต์แอปควรประกาศตัวกรองแยกกันสำหรับงานที่ไม่ซ้ำกันแต่ละอย่างที่ทำได้ เช่น กิจกรรมหนึ่งในแอปแกลเลอรีรูปภาพอาจมีตัวกรอง 2 รายการ คือ 1 ตัวกรอง เพื่อดูรูปภาพ และใช้ตัวกรองอื่นเพื่อแก้ไขรูปภาพ เมื่อกิจกรรมเริ่ม ระบบจะตรวจสอบ Intent และตัดสินใจเลือกลักษณะการทํางานโดยอิงตามข้อมูล ใน Intent (เช่น แสดงการควบคุมเครื่องมือแก้ไขหรือไม่)

ตัวกรอง Intent แต่ละรายการจะกำหนดโดย <intent-filter> ในไฟล์ Manifest ของแอป โดยฝังอยู่ในคอมโพเนนต์แอปที่เกี่ยวข้อง (เช่น ในฐานะ <activity> )

ในแต่ละคอมโพเนนต์ของแอปที่มีองค์ประกอบ <intent-filter> ตั้งค่าอย่างชัดแจ้งสำหรับ android:exported แอตทริบิวต์นี้ระบุว่าแอปอื่นๆ สามารถเข้าถึงคอมโพเนนต์ของแอปได้หรือไม่ ในบางส่วน เช่น กิจกรรมที่มีตัวกรอง Intent LAUNCHER หมวดหมู่ คุณควรตั้งค่าแอตทริบิวต์นี้เป็น true หรือไม่เช่นนั้น การตั้งค่าแอตทริบิวต์นี้เป็น false จะปลอดภัยกว่า

คำเตือน: หากกิจกรรม บริการ หรือการออกอากาศ รีซีฟเวอร์ในแอปของคุณใช้ตัวกรอง Intent และไม่ได้กำหนดค่าอย่างชัดแจ้ง สำหรับ android:exported แอปของคุณไม่สามารถติดตั้งได้ในอุปกรณ์ที่ ใช้ Android 12 ขึ้นไป

ภายใน<intent-filter> คุณสามารถระบุประเภทของ Intent ที่จะยอมรับโดยใช้ จากองค์ประกอบ 3 อย่างนี้

<action>
ประกาศการดำเนินการผ่าน Intent ที่ยอมรับในแอตทริบิวต์ name ค่า ต้องเป็นค่าสตริงตรงตัวของการกระทำ ไม่ใช่ค่าคงที่ของคลาส
<data>
ประกาศประเภทข้อมูลที่ยอมรับโดยใช้แอตทริบิวต์อย่างน้อย 1 รายการที่ระบุหลากหลาย ด้านของ URI ข้อมูล (scheme, host, port, path) และประเภท MIME
<category>
ประกาศหมวดหมู่ Intent ที่ยอมรับในแอตทริบิวต์ name ค่า ต้องเป็นค่าสตริงตรงตัวของการทำงาน ไม่ใช่ค่าคงที่ของคลาส

หมายเหตุ: หากต้องการรับ Intent แบบไม่เจาะจงปลายทาง คุณต้อง ต้องมีฟิลด์ CATEGORY_DEFAULT ในตัวกรอง Intent วิธีการ startActivity() และ startActivityForResult() จะถือว่า Intent ทั้งหมด เหมือนกับว่าได้ประกาศหมวดหมู่ CATEGORY_DEFAULT ไว้ หากคุณไม่ประกาศหมวดหมู่นี้ในตัวกรอง Intent จะไม่มีการแก้ไข Intent แบบไม่เจาะจงปลายทางเป็น กิจกรรมของคุณ

ลองดูตัวอย่างการประกาศกิจกรรมที่มีตัวกรอง Intent เพื่อรับ ACTION_SEND Intent เมื่อประเภทข้อมูลเป็นข้อความ:

<activity android:name="ShareActivity" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

คุณสามารถสร้างตัวกรองที่มีอินสแตนซ์ของ <action> <data> หรือ <category> หากมี คุณต้องมั่นใจว่าคอมโพเนนต์จะจัดการ ชุดค่าผสมขององค์ประกอบตัวกรองเหล่านั้น

เมื่อคุณต้องการจัดการ Intent หลายประเภท การดำเนินการ ข้อมูล และประเภทหมวดหมู่ คุณจะต้องสร้างตัวกรอง Intent หลายรายการ

ระบบจะทดสอบ Intent แบบไม่เจาะจงปลายทางกับตัวกรองโดยเปรียบเทียบความตั้งใจกับแต่ละรายการ องค์ประกอบ 3 อย่าง Intent ต้องผ่านการทดสอบทั้ง 3 รายการจึงจะแสดงไปยังคอมโพเนนต์ได้ หากไม่มีการจับคู่ที่ตรงกันเลย ระบบ Android จะไม่มอบความตั้งใจไปยัง คอมโพเนนต์ แต่เนื่องจากคอมโพเนนต์หนึ่งอาจมีตัวกรอง Intent หลายรายการ Intent ที่มี ไม่ผ่านตัวกรองใดคอมโพเนนต์หนึ่ง อาจทะลุผ่านตัวกรองอื่นได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่ระบบแก้ไข Intent ได้ที่ส่วนด้านล่าง เกี่ยวกับความละเอียดของความตั้งใจ

ข้อควรระวัง: การใช้ตัวกรอง Intent ไม่ใช่วิธีที่ปลอดภัยในการป้องกันไม่ให้แอปอื่นๆ เริ่มทำงาน คอมโพเนนต์ของคุณ แม้ว่าตัวกรอง Intent จะจำกัดคอมโพเนนต์ให้ตอบสนองต่อ Intent แบบไม่เจาะจงปลายทางบางประเภท แอปอื่นอาจเริ่มคอมโพเนนต์ของแอปได้ โดยการใช้เจตนาที่ชัดแจ้งหากนักพัฒนาซอฟต์แวร์กำหนดชื่อคอมโพเนนต์ของคุณ หากสิ่งสำคัญคือมีแต่แอปของคุณเองเท่านั้นที่เริ่มต้นคอมโพเนนต์ได้ อย่าประกาศตัวกรอง Intent ในไฟล์ Manifest แต่ให้ตั้งค่า แอตทริบิวต์ของ exported เป็น "false" สำหรับคอมโพเนนต์นั้น

ในทำนองเดียวกัน เพื่อหลีกเลี่ยงการเรียกใช้แอปพลิเคชันอื่นโดยไม่ตั้งใจ Service โปรดใช้ Intent แบบเจาะจงปลายทางเพื่อเริ่มบริการของคุณเองเสมอ

หมายเหตุ สำหรับกิจกรรมทั้งหมด คุณต้องประกาศตัวกรอง Intent ในไฟล์ Manifest อย่างไรก็ตาม ตัวกรองสำหรับเครื่องรับสัญญาณออกอากาศสามารถลงทะเบียนแบบไดนามิกได้โดยการเรียกใช้ registerReceiver() จากนั้นคุณจะยกเลิกการลงทะเบียนตัวรับกับ unregisterReceiver() ได้ การทำเช่นนี้จะช่วยให้แอปของคุณ เพื่อฟังการออกอากาศเฉพาะในช่วงระยะเวลาที่กำหนดขณะที่แอปของคุณ กำลังทำงาน

ตัวอย่างตัวกรอง

ตัวอย่างในการสาธิตลักษณะการทำงานของตัวกรอง Intent บางรายการมีดังนี้ จากไฟล์ Manifest ของแอปการแชร์ผ่านโซเชียล

<activity android:name="MainActivity" android:exported="true">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity" android:exported="false">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

กิจกรรมแรก MainActivity คือจุดแรกเข้าหลักของแอป ซึ่งเป็นกิจกรรมที่ เปิดขึ้นเมื่อผู้ใช้เปิดแอปเป็นครั้งแรกด้วยไอคอน Launcher ดังนี้

  • การดำเนินการ ACTION_MAIN ระบุว่านี่เป็นจุดแรกเข้าหลักและไม่มีข้อมูล Intent
  • หมวดหมู่ CATEGORY_LAUNCHER บ่งชี้ว่ากิจกรรมนี้ ควรวางในตัวเปิดแอปของระบบ หากองค์ประกอบ <activity> ไม่ได้ระบุไอคอนด้วย icon ระบบจะใช้ไอคอนจาก <application>

ทั้ง 2 สิ่งนี้ต้องจับคู่เข้าด้วยกันเพื่อให้กิจกรรมปรากฏในเครื่องเรียกใช้งานแอป

กิจกรรมที่ 2 ShareActivity มีจุดประสงค์เพื่ออำนวยความสะดวกในการแชร์ข้อความและสื่อ เนื้อหา แม้ว่าผู้ใช้อาจเข้าสู่กิจกรรมนี้โดยไปที่กิจกรรมจาก MainActivity ผู้ใช้ยังสามารถป้อน ShareActivity โดยตรงจากแอปอื่นที่ออกคำสั่งเป็นนัย Intent ตรงกับตัวกรอง Intent 1 จาก 2 รายการ

หมายเหตุ: ประเภท MIME application/vnd.google.panorama360+jpg คือประเภทข้อมูลพิเศษที่ระบุ ภาพพาโนรามา ซึ่งคุณจัดการได้ด้วย Google panorama API

จับคู่ Intent กับแอปอื่นๆ ตัวกรอง Intent

หากแอปอื่นกำหนดเป้าหมายเป็น Android 13 (API ระดับ 33) ขึ้นไป ก็จะสามารถจัดการ Intent ของแอปเฉพาะในกรณีที่ Intent ตรงกับการดำเนินการและหมวดหมู่ของ <intent-filter> ในแอปนั้น หากระบบไม่พบ เกมจะแสดงข้อผิดพลาด ActivityNotFoundException แอปที่ส่งต้องจัดการ ข้อยกเว้นนี้

ในทำนองเดียวกัน หากคุณอัปเดตแอปเพื่อให้กำหนดเป้าหมายเป็น Android 13 ขึ้นไป ระบบจะนำส่ง Intent ทั้งหมดที่มาจากแอปภายนอกไปยัง คอมโพเนนต์ที่ส่งออกมาก็ต่อเมื่อ Intent นั้นตรงกับการดำเนินการ และ หมวดหมู่ขององค์ประกอบ <intent-filter> ที่แอปของคุณประกาศ ลักษณะการทำงานนี้ เกิดขึ้นไม่ว่าแอปที่ส่งเวอร์ชัน SDK เป้าหมายจะเป็นอย่างไรก็ตาม

ในกรณีต่อไปนี้ ระบบจะไม่บังคับใช้การจับคู่ Intent

  • Intent ที่ส่งไปยังคอมโพเนนต์ที่ไม่ได้ประกาศตัวกรอง Intent ใดๆ
  • Intent ที่มาจากภายในแอปเดียวกัน
  • Intent ที่มาจากระบบ นั่นคือ Intent ที่ส่งจาก "UID ของระบบ" (uid=1000) แอประบบรวมถึง system_server และแอปที่ตั้งค่า android:sharedUserId ถึง android.uid.system
  • Intent ที่มาจากรูท

ดูข้อมูลเพิ่มเติมเกี่ยวกับการจับคู่ความตั้งใจ

ใช้ Intent ที่รอดำเนินการ

ออบเจ็กต์ PendingIntent คือ Wrapper ที่อยู่รอบๆ ออบเจ็กต์ Intent วัตถุประสงค์หลักของ PendingIntent คือการให้สิทธิ์กับแอปพลิเคชันในต่างประเทศ เพื่อใช้ Intent ที่มีอยู่ประหนึ่งว่าได้ดำเนินการจาก กระบวนการของแอปเอง

กรณีการใช้งานหลักๆ สำหรับ Intent ที่รอดำเนินการมีดังนี้

  • ประกาศความตั้งใจที่จะดำเนินการเมื่อผู้ใช้ดำเนินการด้วยการแจ้งเตือนของคุณ (NotificationManager ของระบบ Android จะเรียกใช้ Intent)
  • ประกาศความตั้งใจที่จะดำเนินการเมื่อผู้ใช้ดำเนินการ วิดเจ็ตแอป (แอปหน้าจอหลักจะเรียกใช้ Intent)
  • ประกาศความตั้งใจที่จะดำเนินการในอนาคตที่ระบุไว้ (Android AlarmManager ของระบบเรียกใช้ Intent)

เช่นเดียวกับที่ออบเจ็กต์ Intent แต่ละรายการออกแบบมาให้ได้รับการจัดการโดย ประเภทคอมโพเนนต์แอป (Activity, Service หรือ BroadcastReceiver) ดังนั้น PendingIntent ก็ต้องเป็น ที่เราพิจารณาแบบเดียวกัน เมื่อใช้ Intent ที่รอดำเนินการ แอปของคุณจะไม่ เรียกใช้ Intent ด้วยการเรียก เช่น startActivity() คุณต้องประกาศประเภทคอมโพเนนต์ที่ต้องการแทนเมื่อสร้างคอมโพเนนต์ PendingIntent โดยเรียกวิธีการของครีเอเตอร์ที่เกี่ยวข้องดังนี้

เว้นแต่ว่าแอปของคุณจะได้รับ Intent ที่รอดำเนินการจากแอปอื่นๆ วิธีการข้างต้นในการสร้าง PendingIntent น่าจะเป็นเพียงวิธีเดียว PendingIntent วิธีการที่คุณจำเป็นต้องใช้

แต่ละวิธีจะใช้แอปปัจจุบัน Context ค่า Intent ที่ต้องการรวม และแฟล็กอย่างน้อย 1 รายการที่ระบุ ควรใช้ Intent อย่างไร (เช่น สามารถใช้ Intent ได้มากกว่า 1 ครั้งหรือไม่)

หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับการใช้ Intent ที่รอดำเนินการ โปรดดูเอกสารสำหรับแต่ละ ของ Use Case ที่เกี่ยวข้อง เช่น ในการแจ้งเตือน และคำแนะนำ API ของ App Widgets

ระบุการเปลี่ยนแปลง

หากแอปกำหนดเป้าหมายเป็น Android 12 ขึ้นไป คุณต้องระบุ การเปลี่ยนแปลงของออบเจ็กต์ PendingIntent แต่ละรายการที่แอปสร้าง ในการประกาศว่า ออบเจ็กต์ PendingIntent ที่ระบุจะเปลี่ยนแปลงหรือเปลี่ยนแปลงไม่ได้ ให้ใช้ PendingIntent.FLAG_MUTABLE หรือ PendingIntent.FLAG_IMMUTABLE ตามลำดับ

หากแอปพยายามสร้างออบเจ็กต์ PendingIntent โดยไม่ตั้งค่า Flag การเปลี่ยนแปลง ระบบจะแสดงข้อผิดพลาด IllegalArgumentException และ ข้อความต่อไปนี้จะปรากฏใน Logcat

PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.

สร้าง Intent ที่รอดำเนินการที่เปลี่ยนแปลงไม่ได้เมื่อเป็นไปได้

ในกรณีส่วนใหญ่ แอปควรสร้างออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงไม่ได้ เนื่องจาก ที่แสดงในข้อมูลโค้ดต่อไปนี้ หากออบเจ็กต์ PendingIntent เปลี่ยนแปลงไม่ได้ แอปอื่นๆ จะไม่สามารถแก้ไข Intent ในการปรับผลลัพธ์ของการเรียกใช้ Intent

Kotlin

val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)

Java

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE);

อย่างไรก็ตาม กรณีการใช้งานบางอย่างต้องใช้ออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงได้แทน ดังนี้

  • สนับสนุนการดำเนินการตอบกลับโดยตรงใน การแจ้งเตือน การตอบกลับโดยตรงต้องมีการเปลี่ยนแปลงข้อมูลคลิปในออบเจ็กต์ PendingIntent ที่เชื่อมโยงกับการตอบกลับ โดยปกติแล้ว คุณจะร้องขอการเปลี่ยนแปลงนี้โดยการส่ง FILL_IN_CLIP_DATA เป็นธงของ fillIn()
  • การเชื่อมโยงการแจ้งเตือนกับเฟรมเวิร์ก Android Auto โดยใช้ตัวอย่าง CarAppExtender
  • การวางการสนทนาในลูกโป่งโดยใช้อินสแตนซ์ จาก PendingIntent ออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงได้ช่วยให้ระบบนำไปใช้ได้ ค่าสถานะที่ถูกต้อง เช่น FLAG_ACTIVITY_MULTIPLE_TASK และ FLAG_ACTIVITY_NEW_DOCUMENT
  • ขอข้อมูลตำแหน่งอุปกรณ์ด้วยการโทร requestLocationUpdates() หรือ API ที่คล้ายกัน ออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงได้ช่วยให้ระบบเพิ่ม Intent เพิ่มเติมที่แสดงเหตุการณ์ในวงจรสถานที่ตั้ง เหตุการณ์เหล่านี้ประกอบไปด้วย การเปลี่ยนแปลงสถานที่ตั้งและผู้ให้บริการที่พร้อมให้บริการ
  • ตั้งเวลาปลุกโดยใช้ AlarmManager ออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงได้ช่วยให้ระบบเพิ่ม EXTRA_ALARM_COUNT Intent เพิ่มเติม ส่วนเกินนี้แสดงจำนวนครั้งที่การปลุกซ้ำ ถูกเรียกใช้แล้ว ด้วยการใส่ข้อมูลเพิ่มเติมนี้ Intent สามารถแจ้งเตือน แอปว่าการปลุกซ้ำถูกเรียกใช้หลายครั้งหรือไม่ เช่น เมื่ออุปกรณ์อยู่ในโหมดสลีป

เราขอแนะนำอย่างยิ่งหากแอปของคุณสร้างออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงได้ คุณใช้เจตนาอย่างชัดแจ้งและกรอกข้อมูล ComponentName วิธีนี้จะทำให้เมื่อใดก็ได้ แอปอื่นจะเรียกใช้ PendingIntent และตัวควบคุมบัตรกลับไปยังแอปของคุณ คอมโพเนนต์เดียวกันในแอปจะเริ่มต้นเสมอ

ใช้ Intent แบบเจาะจงภายใน Intent ที่รอดำเนินการ

หากต้องการระบุวิธีที่แอปอื่นๆ สามารถใช้ Intent ที่รอดำเนินการของแอปได้ดีขึ้น โปรดทำดังนี้ ใส่ Intent ที่รอดำเนินการรอบๆ เจตนาที่ชัดแจ้งไว้ด้วย เพื่อช่วยทำตามแนวทางปฏิบัติแนะนำดังกล่าว โปรดทำตามขั้นตอนต่อไปนี้

  1. ตรวจสอบว่าฟิลด์การดำเนินการ แพ็กเกจ และคอมโพเนนต์ของ Intent พื้นฐาน ได้รับการตั้งค่าแล้ว
  2. ใช้ FLAG_IMMUTABLE, เพิ่มเข้ามาใน Android 6.0 (API ระดับ 23) เพื่อสร้าง Intent ที่รอดำเนินการ แฟล็กนี้ ป้องกันไม่ให้แอปที่ได้รับ PendingIntent ป้อนข้อมูล พร็อพเพอร์ตี้ที่ไม่ได้สร้างไว้ หากminSdkVersionของแอปคือ 22 หรือต่ำกว่า คุณสามารถมอบความปลอดภัยและความเข้ากันได้ร่วมกันได้ โดยใช้โค้ดต่อไปนี้

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

ความละเอียดของความตั้งใจ

เมื่อระบบได้รับ Intent ที่ชัดแจ้งเพื่อเริ่มกิจกรรม ระบบจะค้นหา กิจกรรมที่ดีที่สุดสำหรับ Intent นั้นโดยเปรียบเทียบกับตัวกรอง Intent ตาม 3 ด้านต่อไปนี้

  • เริ่มถ่ายเลย
  • ข้อมูล (ทั้ง URI และประเภทข้อมูล)
  • หมวดหมู่

ส่วนต่อไปนี้จะอธิบายวิธีจับคู่ Intent กับคอมโพเนนต์ที่เหมาะสม ตามการประกาศตัวกรอง Intent ในไฟล์ Manifest ของแอป

การทดสอบการดำเนินการ

ในการระบุการดำเนินการของ Intent ที่ยอมรับ ตัวกรอง Intent จะประกาศค่าเป็น 0 ขึ้นไปได้ <action> ดังที่แสดงในตัวอย่างต่อไปนี้

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

หากต้องการผ่านตัวกรองนี้ การดำเนินการที่ระบุไว้ใน Intent ต้องตรงกับหนึ่งในการดำเนินการที่ระบุไว้ในตัวกรอง

หากตัวกรองไม่ได้ระบุการดำเนินการใดๆ จะไม่มีการดำเนินการใดๆ Intent ที่จะจับคู่ ดังนั้น Intent ทั้งหมดจึงไม่ผ่านการทดสอบ อย่างไรก็ตาม หาก Intent ไม่ได้ระบุการดำเนินการ แต่จะผ่านการทดสอบตราบใดที่ตัวกรอง มีการดำเนินการอย่างน้อย 1 รายการ

การทดสอบหมวดหมู่

ในการระบุหมวดหมู่ Intent ที่ยอมรับ ตัวกรอง Intent จะประกาศเป็น 0 ขึ้นไปได้ <category> ดังที่แสดงในตัวอย่างต่อไปนี้

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

เพื่อให้ผ่านการทดสอบหมวดหมู่ ทุกหมวดหมู่ใน Intent ต้องตรงกับหมวดหมู่ในตัวกรอง ไม่จำเป็นต้องย้อนกลับ ตัวกรอง Intent อาจ มีหมวดหมู่มากกว่าที่ระบุไว้ใน Intent และ ยังผ่าน Intent อยู่ ดังนั้น Intent ที่ไม่มีหมวดหมู่ จะผ่านการทดสอบนี้เสมอ โดยไม่คำนึงว่าจะประกาศหมวดหมู่ใดในตัวกรอง

หมายเหตุ Android จะใช้หมวดหมู่ CATEGORY_DEFAULT โดยอัตโนมัติ ไปยัง Intent แบบไม่เจาะจงปลายทางทั้งหมดที่ส่งไปยัง startActivity() และ startActivityForResult() หากคุณต้องการให้กิจกรรมได้รับ Intent แบบไม่เจาะจงปลายทาง รวมหมวดหมู่สำหรับ "android.intent.category.DEFAULT" ในตัวกรอง Intent เช่น ที่แสดงในตัวอย่าง <intent-filter> ก่อนหน้านี้

การทดสอบข้อมูล

ในการระบุข้อมูล Intent ที่ยอมรับ ตัวกรอง Intent จะประกาศค่าเป็น 0 ขึ้นไปได้ <data> ดังที่แสดงในตัวอย่างต่อไปนี้

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

แต่ละ<data> สามารถระบุโครงสร้าง URI และประเภทข้อมูล (ประเภทสื่อ MIME) แต่ละส่วนของ URI จะเป็น แอตทริบิวต์: scheme, host, port และ path:

<scheme>://<host>:<port>/<path>

ตัวอย่างต่อไปนี้แสดงค่าที่เป็นไปได้สำหรับแอตทริบิวต์เหล่านี้

content://com.example.project:200/folder/subfolder/etc

ใน URI นี้ รูปแบบคือ content โฮสต์คือ com.example.project พอร์ตคือ 200 และเส้นทางคือ folder/subfolder/etc

แอตทริบิวต์แต่ละรายการเหล่านี้ไม่บังคับในองค์ประกอบ <data> แต่มีทรัพยากร Dependency แบบเชิงเส้น เช่น

  • หากไม่ได้ระบุรูปแบบ ระบบจะไม่สนใจโฮสต์
  • หากไม่ได้ระบุโฮสต์ ระบบจะไม่สนใจพอร์ต
  • หากไม่ได้ระบุทั้งรูปแบบและโฮสต์ ระบบจะไม่สนใจเส้นทาง

เมื่อเปรียบเทียบ URI ใน Intent กับข้อกำหนด URI ในตัวกรอง ระบบจะเปรียบเทียบเฉพาะในส่วนของ URI ที่รวมอยู่ในตัวกรองเท่านั้น เช่น

  • หากตัวกรองระบุเฉพาะสคีม URI ทั้งหมดที่มีรูปแบบนั้นตรงกัน ตัวกรอง
  • หากตัวกรองระบุรูปแบบและสิทธิ์แต่ไม่มีเส้นทาง URI ทั้งหมด ที่มีรูปแบบและสิทธิ์เดียวกันจะผ่านตัวกรอง โดยไม่คำนึงถึงเส้นทาง
  • หากตัวกรองระบุสคีม สิทธิ์ และเส้นทาง จะมีเพียง URI ที่มีรูปแบบเดียวกันเท่านั้น ที่เหมาะสม และเส้นทางการผ่านตัวกรอง

หมายเหตุ: ข้อกำหนดเส้นทาง มีเครื่องหมายดอกจัน (*) เพื่อกำหนดให้ชื่อเส้นทางมีการจับคู่ได้บางส่วนเท่านั้น

การทดสอบข้อมูลจะเปรียบเทียบทั้ง URI และประเภท MIME ใน Intent กับ URI และประเภท MIME ที่ระบุในตัวกรอง โดยมีกฎดังนี้

  1. Intent ที่ไม่มี URI หรือประเภท MIME ไม่ได้ส่งผ่านพารามิเตอร์ ให้ทดสอบต่อเมื่อตัวกรองไม่ได้ระบุ URI หรือประเภท MIME ไว้
  2. Intent ที่มี URI แต่ไม่มีประเภท MIME (ไม่ระบุอย่างชัดเจนหรืออนุมานได้จาก URI) จะผ่านการทดสอบก็ต่อเมื่อ URI ตรงกับรูปแบบ URI ของตัวกรอง และในทำนองเดียวกันตัวกรองจะไม่ระบุประเภท MIME
  3. Intent ที่มีประเภท MIME แต่ไม่มี URI ผ่านการทดสอบ เฉพาะเมื่อตัวกรองแสดงประเภท MIME เดียวกันและไม่ได้ระบุรูปแบบ URI
  4. Intent ที่มีทั้ง URI และประเภท MIME (อาจไม่เหมาะสมหรือสามารถอนุมานได้จาก URI) จะส่งในส่วนของประเภท MIME ของการทดสอบก็ต่อเมื่อ ประเภทตรงกับประเภทที่แสดงในตัวกรอง การทดสอบดังกล่าวผ่านส่วน URI ของการทดสอบ ในกรณีที่ URI ตรงกับ URI ในตัวกรอง หรือมี content: หรือ file: URI และตัวกรองไม่ได้ระบุ URI กล่าวคือ จะถือว่าคอมโพเนนต์รองรับข้อมูล content: และ file: หาก ตัวกรองจะแสดงรายการประเภท MIME เท่านั้น

หมายเหตุ: หาก Intent ระบุประเภท URI หรือ MIME การทดสอบข้อมูลจะ จะล้มเหลวหากไม่มีองค์ประกอบ <data> ใน <intent-filter>

กฎข้อสุดท้าย กฎ (ง) นี้แสดงถึงสิ่งที่คาดหวัง คอมโพเนนต์สามารถรับข้อมูลในเครื่องจากผู้ให้บริการไฟล์หรือผู้ให้บริการเนื้อหา ตัวกรองของตัวกรองจึงแสดงได้เฉพาะประเภทข้อมูลเท่านั้น และไม่จำเป็นต้องระบุ ตั้งชื่อรูปแบบ content: และ file: ตัวอย่างต่อไปนี้แสดงกรณีทั่วไปที่องค์ประกอบ <data> บอก Android ว่าคอมโพเนนต์สามารถรับข้อมูลรูปภาพจากเนื้อหาได้ ของผู้ให้บริการ และแสดงไว้ดังนี้

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

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

การกำหนดค่าทั่วไปอีกแบบหนึ่งคือตัวกรองที่มีรูปแบบและประเภทข้อมูล สำหรับ ตัวอย่างเช่น <data> ดังเช่นตัวอย่างต่อไปนี้บอกให้ Android ทราบว่า คอมโพเนนต์สามารถดึงข้อมูลวิดีโอจากเครือข่ายเพื่อนำมาใช้งาน ดังนี้

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

การจับคู่ Intent

ระบบจะจับคู่ Intent กับตัวกรอง Intent ที่ไม่ใช่เพียงเพื่อค้นพบเป้าหมาย สำหรับเปิดใช้งาน และยังพบบางอย่างเกี่ยวกับชุดของ คอมโพเนนต์ต่างๆ ในอุปกรณ์ ตัวอย่างเช่น แอป Home จะเติมตัวเปิดแอป ด้วยการค้นหากิจกรรมทั้งหมดด้วยตัวกรอง Intent ที่ระบุ ACTION_MAIN การดำเนินการและ CATEGORY_LAUNCHERหมวดหมู่ การจับคู่จะสําเร็จก็ต่อเมื่อการกระทําและหมวดหมู่ใน Intent ตรงกัน ตามตัวกรองตามที่อธิบายไว้ในเอกสารสำหรับ IntentFilter

แอปพลิเคชันของคุณใช้การจับคู่ Intent ได้ในลักษณะที่คล้ายกับการทำงานของแอป Home PackageManager มีชุดของ query...() ซึ่งแสดงผลคอมโพเนนต์ทั้งหมดที่สามารถยอมรับความตั้งใจที่เฉพาะเจาะจงและ ชุดเมธอดของ resolve...() ที่คล้ายกันซึ่งตัดสินว่าวิธีที่ดีที่สุด ในการตอบสนองต่อความตั้งใจ ตัวอย่างเช่น queryIntentActivities() แสดงรายการกิจกรรมทั้งหมดที่สามารถทำได้ ความตั้งใจที่ส่งผ่านเป็นอาร์กิวเมนต์ และ queryIntentServices() แสดงรายการบริการที่คล้ายกัน ไม่มีวิธีใดเปิดใช้งานคอมโพเนนต์ เพียงแค่ระบุรายการข้อมูลที่ ตอบกลับได้ มีวิธีที่คล้ายกัน queryBroadcastReceivers() สำหรับ Broadcast Receiver