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

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

ในกรณีของ 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 ของข้อมูลนั้น โดยทั่วไปแล้ว ประเภทของข้อมูลที่ระบุจะขึ้นอยู่กับการดำเนินการของ Intent เช่น หากการดำเนินการคือ 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() เนื่องจากแต่ละรายการจะทำให้ค่าของอีกรายการเป็น Null ใช้ 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 โดยอาจสั่งให้ระบบ 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) จะจัดหา Context ของแอปและออบเจ็กต์ Class ให้กับคอมโพเนนต์ ดังนั้น ความตั้งใจนี้จึงเริ่มคลาส 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 เพื่อไปยังส่วนประกอบต่างๆ ภายในแอป หรือเพื่อดำเนินการในนามของแอปอื่น เพื่อปรับปรุงความปลอดภัยของแพลตฟอร์ม 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 เป็นส่วนเสริมได้ Intent รอดำเนินการนี้ เรียกใช้คอมโพเนนต์ในแอปของคุณ แต่คอมโพเนนต์นี้ไม่ได้ส่งออก
  2. เมื่อได้รับ Intent ของแอปของคุณ แอปอื่นจะดึงออบเจ็กต์ PendingIntentที่ซ้อนกันออกมา
  3. แอปอื่นจะเรียกใช้เมธอด send() ในออบเจ็กต์ PendingIntent
  4. หลังจากส่งการควบคุมกลับไปยังแอปแล้ว ระบบจะเรียกใช้ Intent ที่รอดำเนินการโดยใช้บริบทของแอป

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

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

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

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

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

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

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

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

เช่นเดียวกับที่ออบเจ็กต์ Intent แต่ละรายการได้รับการออกแบบมาให้คอมโพเนนต์แอปประเภทหนึ่งๆ (ไม่ว่าจะเป็น Activity, Service หรือ BroadcastReceiver) จัดการ PendingIntent ก็ต้องสร้างขึ้นโดยคำนึงถึงสิ่งเดียวกัน เมื่อใช้ Intent รอดำเนินการ แอปจะไม่เรียกใช้ 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

เพื่อกำหนดวิธีที่แอปอื่นๆ สามารถใช้ 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 จะประกาศองค์ประกอบ <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 ทั้งหมดที่มีรูปแบบและผู้มีอำนาจเดียวกันจะผ่านตัวกรอง ไม่ว่าจะมีเส้นทางใดก็ตาม
  • หากตัวกรองระบุรูปแบบ, Authority และเส้นทาง เฉพาะ URI ที่มีรูปแบบ, Authority และเส้นทางเดียวกันเท่านั้นที่จะผ่านตัวกรอง

หมายเหตุ: การระบุเส้นทางสามารถมีเครื่องหมายดอกจัน (*) ซึ่งเป็นไวลด์การ์ดเพื่อกำหนดให้ชื่อเส้นทางตรงกันเพียงบางส่วนเท่านั้น

การทดสอบข้อมูลจะเปรียบเทียบทั้ง 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