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

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

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

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

    หากต้องการรับผลลัพธ์จากกิจกรรมเมื่อเสร็จสิ้นแล้ว ให้เรียกใช้ startActivityForResult() กิจกรรมของคุณจะได้รับผลลัพธ์ เป็นIntentออบเจ็กต์แยกต่างหากใน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 วิธีส่ง Intent แบบไม่เจาะจงปลายทาง ผ่านระบบเพื่อเริ่มกิจกรรมอื่น [1] กิจกรรม A สร้าง Intent พร้อมคำอธิบายการดำเนินการและส่งไปยัง startActivity() [2] ระบบ Android จะค้นหาตัวกรอง Intent ที่ตรงกับ Intent ในแอปทั้งหมด เมื่อพบรายการที่ตรงกัน [3] ระบบ จะเริ่มกิจกรรมการจับคู่ (กิจกรรม B) โดยเรียกใช้เมธอด onCreate() และส่ง Intent ไปให้

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

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

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

การสร้าง Intent

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

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

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

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

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

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

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

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

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

ACTION_VIEW
ใช้การดำเนินการนี้ใน Intent ที่มี startActivity() เมื่อคุณมีข้อมูลบางอย่างที่ กิจกรรมสามารถแสดงต่อผู้ใช้ได้ เช่น รูปภาพที่จะดูในแอปแกลเลอรี หรือที่อยู่ที่จะ ดูในแอปแผนที่
ACTION_SEND
คุณควรใช้เจตนาที่เรียกอีกอย่างว่าเจตนาแชร์ในเจตนาที่มี 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";
ข้อมูล
URI (ออบเจ็กต์ Uri) ที่อ้างอิงถึงข้อมูลที่จะ ดำเนินการ และ/หรือ ประเภท MIME ของข้อมูลนั้น โดยทั่วไปแล้ว ประเภทของข้อมูลที่ให้จะขึ้นอยู่กับการดำเนินการของเจตนา เช่น หากการดำเนินการคือ ACTION_EDIT ข้อมูลควรมี URI ของเอกสารที่จะแก้ไข

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

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

ข้อควรระวัง: หากต้องการตั้งค่าทั้ง URI และประเภท MIME อย่าเรียกใช้ setData() และ setType() เนื่องจากแต่ละรายการจะลบล้างค่าของอีกรายการหนึ่ง ใช้ setDataAndType() เพื่อตั้งค่าทั้ง URI และประเภท MIME เสมอ

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

ดูรายการหมวดหมู่ทั้งหมดได้ที่Intentคำอธิบายคลาส

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

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

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

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

เช่น เมื่อสร้าง Intent เพื่อส่งอีเมลด้วย ACTION_SEND คุณจะระบุผู้รับ to ด้วยคีย์ 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

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

ดูข้อมูลเพิ่มเติมได้ที่วิธีการ setFlags()

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

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) จะจัดหาออบเจ็กต์ Class ให้กับแอป Context และคอมโพเนนต์ ดังนั้น ความตั้งใจนี้จึงเริ่มคลาส DownloadService ในแอปอย่างชัดเจน

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

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

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

เช่น หากคุณมีเนื้อหาที่ต้องการให้ผู้ใช้แชร์กับผู้อื่น ให้สร้าง Intent ด้วย ACTION_SEND action และเพิ่มส่วนเสริมที่ระบุเนื้อหาที่จะแชร์ เมื่อคุณเรียกใช้ 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 ประเภทนี้ได้ (Intent ที่มีการดำเนินการ ACTION_SEND และมีข้อมูล "text/plain") หากมีเพียงแอปเดียวที่จัดการได้ แอปนั้นจะเปิดขึ้นทันทีและได้รับ Intent หากไม่มีแอปอื่นจัดการได้ แอปของคุณจะตรวจพบ ActivityNotFoundException ที่เกิดขึ้น หากมีกิจกรรมหลายรายการที่ยอมรับ Intent ระบบจะแสดงกล่องโต้ตอบ เช่น กล่องโต้ตอบที่แสดงในรูปที่ 2 เพื่อให้ผู้ใช้เลือกแอปที่จะใช้ได้

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

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

บังคับให้ใช้ตัวเลือกแอป

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

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

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

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

ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีระบุสถานการณ์นี้และทำการเปลี่ยนแปลงในแอปได้ในบล็อกโพสต์เกี่ยวกับ Android Nesting Intents บน 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 ที่ไม่ปลอดภัยและการละเมิด StrictMode

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

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

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

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

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

รูปที่ 2 แผนภาพการสื่อสารระหว่างแอปเมื่อใช้ PendingIntent ที่ซ้อนกัน

การรับ Intent แบบไม่เจาะจงปลายทาง

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

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

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

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

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

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

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

<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 เพื่อรับ Intent ACTION_SEND เมื่อประเภทข้อมูลเป็นข้อความ

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

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

ระบบจะทดสอบ Intent แบบไม่เจาะจงปลายทาง กับตัวกรองโดยการเปรียบเทียบ Intent กับองค์ประกอบทั้ง 3 อย่าง Intent ต้องผ่านการทดสอบทั้ง 3 รายการจึงจะส่งไปยังคอมโพเนนต์ได้ หากจับคู่ไม่สำเร็จแม้แต่รายการเดียว ระบบ Android จะไม่ส่ง Intent ไปยัง คอมโพเนนต์ อย่างไรก็ตาม เนื่องจากคอมโพเนนต์อาจมีตัวกรอง 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 แสดงว่าควรวางไอคอนของกิจกรรมนี้ ไว้ใน App Launcher ของระบบ หากองค์ประกอบ <activity> ไม่ได้ระบุไอคอนด้วย icon ระบบจะใช้ไอคอนจากองค์ประกอบ <application>

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

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

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

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

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

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

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

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

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

การใช้ความตั้งใจที่รอดำเนินการ

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

กรณีการใช้งานที่สำคัญสำหรับ PendingIntent มีดังนี้

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

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

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

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

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

ระบุความสามารถในการเปลี่ยนแปลงได้

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

หากแอปพยายามสร้างออบเจ็กต์ PendingIntent โดยไม่ได้ตั้งค่าสถานะความสามารถในการเปลี่ยนแปลงได้ ระบบจะแสดง 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.

สร้าง PendingIntent ที่เปลี่ยนแปลงไม่ได้ทุกครั้งที่ทำได้

ในกรณีส่วนใหญ่ แอปควรสร้างออบเจ็กต์ 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);

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

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

ใช้ Intent ที่ชัดเจนภายใน PendingIntent

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

  1. ตรวจสอบว่ามีการตั้งค่าฟิลด์การดำเนินการ แพ็กเกจ และคอมโพเนนต์ของ Intent พื้นฐาน
  2. ใช้ FLAG_IMMUTABLE ซึ่งเพิ่มใน Android 6.0 (ระดับ API 23) เพื่อสร้าง PendingIntent แฟล็กนี้ จะป้องกันไม่ให้แอปที่ได้รับ 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 จะประกาศองค์ประกอบ <action> ได้ตั้งแต่ 0 รายการขึ้นไป ดังตัวอย่างต่อไปนี้

<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 สามารถประกาศองค์ประกอบ <category>ได้ตั้งแต่ 0 รายการขึ้นไป ดังที่แสดงในตัวอย่างต่อไปนี้

<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 สามารถประกาศองค์ประกอบ <data> ได้ตั้งแต่ 0 รายการขึ้นไป ดังตัวอย่างต่อไปนี้

<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> แต่มีความขึ้นต่อกันแบบเชิงเส้น ดังนี้

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

เมื่อเปรียบเทียบ 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 ในตัวกรองหรือไม่ หรือหากมี URI content: หรือ file: และตัวกรองไม่ได้ระบุ URI กล่าวคือ ระบบจะถือว่าคอมโพเนนต์รองรับข้อมูล content: และ file: หากรายการตัวกรองของคอมโพเนนต์มีประเภท MIME เพียงประเภทเดียว

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

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

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

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

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

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

การจับคู่ตามความตั้งใจ

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

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