Intent คือออบเจ็กต์การรับส่งข้อความที่คุณใช้เพื่อขอการดำเนินการจากคอมโพเนนต์แอปอื่นได้
แม้ว่า Intent จะช่วยอำนวยความสะดวกในการสื่อสารระหว่างคอมโพเนนต์ได้หลายวิธี แต่ก็มีกรณีการใช้งานพื้นฐาน 3 กรณีดังนี้
- การเริ่มกิจกรรม
Activityแสดงหน้าจอเดียวในแอป คุณสามารถเริ่มอินสแตนซ์ใหม่ของActivityโดยส่งIntentไปยังstartActivity()Intentจะอธิบายกิจกรรมที่จะเริ่มและมีข้อมูลที่จำเป็นหากต้องการรับผลลัพธ์จากกิจกรรมเมื่อกิจกรรมเสร็จสิ้น ให้เรียกใช้
startActivityForResult()กิจกรรมของคุณจะได้รับผลลัพธ์เป็นออบเจ็กต์Intentแยกต่างหากใน CallbackonActivityResult()ของกิจกรรม ดูข้อมูลเพิ่มเติมได้ที่คู่มือกิจกรรม - การเริ่มให้บริการ
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
Intent มี 2 ประเภท ได้แก่
- Explicit 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 ไปยังคอมโพเนนต์แอปที่กำหนดโดยชื่อคอมโพเนนต์เท่านั้น หากไม่มีชื่อคอมโพเนนต์ Intent จะเป็นแบบโดยนัย และระบบจะตัดสินใจว่าคอมโพเนนต์ใดควรได้รับ Intent โดยอิงตามข้อมูล Intent อื่นๆ (เช่น การดำเนินการ ข้อมูล และหมวดหมู่ที่อธิบายไว้ด้านล่าง) หากต้องการเริ่มคอมโพเนนต์ที่เฉพาะเจาะจงในแอป คุณควรระบุชื่อคอมโพเนนต์
หมายเหตุ: เมื่อเริ่ม
Serviceให้ระบุชื่อคอมโพเนนต์เสมอ ไม่เช่นนั้น คุณจะไม่แน่ใจว่าบริการใดจะตอบสนองต่อ Intent และผู้ใช้จะไม่เห็นว่าบริการใดเริ่มทำงานช่องนี้ของ
Intentเป็นออบเจ็กต์ComponentNameซึ่งคุณระบุได้โดยใช้ชื่อคลาสที่สมบูรณ์ในตัวเองของคอมโพเนนต์เป้าหมาย รวมถึงชื่อแพ็กเกจของแอป เช่นcom.example.ExampleActivityคุณสามารถตั้งชื่อคอมโพเนนต์ด้วยsetComponent(),setClass(),setClassName()หรือด้วยคอนสตรัคเตอร์Intent - การดำเนินการ
- สตริงที่ระบุการดำเนินการทั่วไปที่จะทำ (เช่น view หรือ pick)
ในกรณีของ Intent การออกอากาศ นี่คือการดำเนินการที่เกิดขึ้นและกำลังรายงาน การดำเนินการจะเป็นตัวกำหนดโครงสร้างของ Intent ที่เหลือเป็นส่วนใหญ่ โดยเฉพาะข้อมูลที่อยู่ในข้อมูลและข้อมูลเพิ่มเติม
คุณสามารถระบุการดำเนินการของคุณเองเพื่อใช้โดย 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";
- ข้อมูล
- URI (ออบเจ็กต์
Uri) ที่อ้างอิงข้อมูลที่จะดำเนินการและ/หรือประเภท MIME ของข้อมูลนั้น โดยทั่วไปแล้ว ประเภทของข้อมูลที่ระบุจะกำหนดโดยการดำเนินการของ Intent เช่น หากการดำเนินการคือACTION_EDITข้อมูลควรมี URI ของเอกสารที่จะแก้ไขเมื่อสร้าง Intent สิ่งสำคัญคือต้องระบุประเภทข้อมูล (ประเภท MIME) นอกเหนือจาก URI ตัวอย่างเช่น กิจกรรมที่แสดงรูปภาพได้อาจเล่นไฟล์เสียงไม่ได้ แม้ว่ารูปแบบ URI จะคล้ายกันก็ตาม การระบุประเภท MIME ของข้อมูลจะช่วยให้ระบบ Android ค้นหาคอมโพเนนต์ที่ดีที่สุดเพื่อรับ Intent ของคุณ อย่างไรก็ตาม บางครั้งอาจอนุมานประเภท MIME ได้จาก URI โดยเฉพาะเมื่อข้อมูลเป็น URI
content:URIcontent:บ่งชี้ว่าข้อมูลอยู่ในอุปกรณ์และควบคุมโดย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 ยังให้ข้อมูลต่อไปนี้ได้ด้วย
- Extras
- คู่คีย์-ค่าที่มีข้อมูลเพิ่มเติมที่จำเป็นต่อการดำเนินการตามคำขอ
เช่นเดียวกับการดำเนินการบางอย่างที่ใช้ URI ข้อมูลบางประเภท การดำเนินการบางอย่างก็ใช้ข้อมูลเพิ่มเติมบางอย่างด้วย
คุณสามารถเพิ่มข้อมูลเพิ่มเติมด้วยเมธอด
putExtra()ต่างๆ ซึ่งแต่ละเมธอดจะยอมรับพารามิเตอร์ 2 รายการ ได้แก่ ชื่อคีย์และค่า นอกจากนี้ คุณยังสร้างออบเจ็กต์Bundleที่มีข้อมูลเพิ่มเติมทั้งหมด แล้วแทรกBundleในIntentด้วยputExtras()ได้ด้วยตัวอย่างเช่น เมื่อสร้าง Intent เพื่อส่งอีเมลด้วย
ACTION_SENDคุณสามารถระบุผู้รับ to ด้วยคีย์EXTRA_EMAILและระบุ subject ด้วยคีย์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()
ตัวอย่างความตั้งใจที่ชัดเจน
Explicit Intent คือ Intent ที่คุณใช้เพื่อเปิดคอมโพเนนต์แอปที่เฉพาะเจาะจง เช่น กิจกรรมหรือบริการหนึ่งๆ ในแอป หากต้องการสร้าง Explicit Intent ให้กำหนดชื่อคอมโพเนนต์สำหรับออบเจ็กต์ Intent ส่วนพร็อพเพอร์ตี้ Intent อื่นๆ ทั้งหมดเป็นแบบไม่บังคับ
ตัวอย่างเช่น หากคุณสร้างบริการในแอปชื่อ DownloadService ซึ่งออกแบบมาเพื่อดาวน์โหลดไฟล์จากเว็บ คุณสามารถเริ่มบริการดังกล่าวด้วยโค้ดต่อไปนี้
Kotlin
// Executed in an Activity, so 'this' is theContext// 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 theContext// 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() ด้วย Intent ดังกล่าว ผู้ใช้จะเลือกแอปที่จะแชร์เนื้อหาได้
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
- แอปของคุณแยกแพ็กเกจ Intent ที่ฝังอยู่จากข้อมูลเพิ่มเติมของ Intent ที่ส่งมา
- แอปของคุณจะเริ่มคอมโพเนนต์แอปทันทีโดยใช้ 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 อย่างมีความรับผิดชอบมากขึ้น
โปรดทำตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อลดโอกาสที่จะเกิดการเปิด Intent ที่ไม่ปลอดภัยและการละเมิด StrictMode
คัดลอกเฉพาะข้อมูลเพิ่มเติมที่จำเป็นภายใน Intent และทำการตรวจสอบและล้างข้อมูลที่จำเป็น แอปของคุณอาจคัดลอกข้อมูลเพิ่มเติมจาก Intent หนึ่งไปยังอีก Intent หนึ่งที่ใช้เพื่อเปิดคอมโพเนนต์ใหม่ ซึ่งจะเกิดขึ้นเมื่อแอปเรียกใช้ putExtras(Intent) หรือ putExtras(Bundle)
หากแอปของคุณดำเนินการอย่างใดอย่างหนึ่งเหล่านี้ ให้คัดลอกเฉพาะข้อมูลเพิ่มเติมที่คอมโพเนนต์ที่เป็นผู้รับคาดหวัง หาก Intent อื่น (ที่ได้รับสำเนา) เปิดคอมโพเนนต์ที่ไม่ได้ส่งออก ให้ตรวจสอบและตรวจสอบความถูกต้องของข้อมูลเพิ่มเติมก่อนคัดลอกไปยัง Intent ที่เปิดคอมโพเนนต์
อย่าส่งออกคอมโพเนนต์ของแอปโดยไม่จำเป็น ตัวอย่างเช่น หากคุณตั้งใจจะเปิดคอมโพเนนต์แอปโดยใช้ Intent ที่ฝังภายใน ให้ตั้งค่าแอตทริบิวต์ android:exported ของคอมโพเนนต์นั้นเป็น false
ใช้ PendingIntent แทน Intent ที่ฝัง ด้วยวิธีนี้ เมื่อแอปอื่นแยก PendingIntent ของ Intent ที่มีอยู่ แอปอื่นจะเปิด PendingIntent โดยใช้ข้อมูลประจำตัวของแอปคุณได้ การกำหนดค่านี้ช่วยให้แอปอื่นเปิดคอมโพเนนต์ใดๆ ได้อย่างปลอดภัย รวมถึงคอมโพเนนต์ที่ไม่ได้ส่งออกในแอปของคุณ
แผนภาพในรูปที่ 2 แสดงวิธีที่ระบบส่งต่อการควบคุมจากแอป (ไคลเอ็นต์) ของคุณไปยังแอป (บริการ) อื่น และกลับมายังแอปของคุณ
- แอปของคุณสร้าง Intent ที่เรียกใช้กิจกรรมในแอปอื่น ภายใน Intent นั้น คุณเพิ่มออบเจ็กต์
PendingIntentเป็นส่วนเสริม Intent รอดำเนินการนี้เรียกใช้คอมโพเนนต์ในแอปของคุณ แต่คอมโพเนนต์นี้ไม่ได้ส่งออก - เมื่อได้รับ Intent ของแอปแล้ว แอปอีกแอปหนึ่งจะดึงออบเจ็กต์
PendingIntentที่ฝังอยู่ออกมา - แอปอื่นจะเรียกใช้เมธอด
send()ในออบเจ็กต์PendingIntent - หลังจากส่งการควบคุมกลับไปยังแอปของคุณแล้ว ระบบจะเรียกใช้ Intent ที่รอดำเนินการโดยใช้บริบทของแอป
รูปที่ 2 แผนภาพการสื่อสารระหว่างแอปเมื่อใช้ Intent รอดำเนินการที่ซ้อนกัน
การรับ 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
อย่างไรก็ตาม คุณสามารถลงทะเบียนตัวกรองสำหรับ Broadcast Receiver แบบไดนามิกได้โดยเรียกใช้ registerReceiver() จากนั้นคุณจะยกเลิกการลงทะเบียนอุปกรณ์รับสัญญาณกับ unregisterReceiver() ได้ การทำเช่นนี้จะช่วยให้แอปของคุณฟังการออกอากาศที่เฉพาะเจาะจงได้ในช่วงเวลาที่ระบุเท่านั้นขณะที่แอปทำงานอยู่
ตัวอย่างของตัวกรอง
ตัวอย่างจากไฟล์ Manifest ของแอปการแชร์ผ่านโซเชียลต่อไปนี้จะแสดงลักษณะการทำงานบางอย่างของตัวกรอง Intent
<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 ตัวใดตัวหนึ่งจาก 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 ที่ส่งจาก "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.getActivity()สำหรับIntentที่เริ่มต้นActivityPendingIntent.getService()สำหรับIntentที่เริ่มServicePendingIntent.getBroadcast()สำหรับIntentที่เริ่มBroadcastReceiver
เว้นแต่ว่าแอปของคุณจะรับ Intent รอดำเนินการจากแอปอื่นๆ
วิธีการข้างต้นในการสร้าง PendingIntent อาจเป็นเพียงวิธีการเดียวของ PendingIntent ที่คุณจำเป็นต้องใช้
แต่ละเมธอดจะใช้ Context ของแอปปัจจุบัน, Intent ที่คุณต้องการรวม และแฟล็กอย่างน้อย 1 รายการที่ระบุวิธีใช้ Intent (เช่น Intent สามารถใช้ได้มากกว่า 1 ครั้งหรือไม่)
ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ Intent ที่รอดำเนินการได้ในเอกสารประกอบสำหรับกรณีการใช้งานที่เกี่ยวข้องแต่ละกรณี เช่น ในคู่มือ 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.
สร้าง 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);
อย่างไรก็ตาม 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 Extra ที่แสดงเหตุการณ์ในวงจรของตำแหน่งได้ เหตุการณ์เหล่านี้รวมถึงการเปลี่ยนแปลงสถานที่ตั้งและการที่ผู้ให้บริการพร้อมให้บริการ - การตั้งเวลาปลุกโดยใช้
AlarmManagerออบเจ็กต์PendingIntentที่เปลี่ยนแปลงได้ช่วยให้ระบบเพิ่ม Intent ExtraEXTRA_ALARM_COUNTได้ ข้อมูลเพิ่มเติมนี้แสดงจำนวนครั้งที่มีการทริกเกอร์การปลุกซ้ำ เมื่อมีข้อมูลเพิ่มเติมนี้ Intent จะแจ้งแอปได้อย่างถูกต้องว่ามีการทริกเกอร์การปลุกซ้ำหลายครั้งหรือไม่ เช่น เมื่ออุปกรณ์อยู่ในโหมดสลีป
หากแอปสร้างออบเจ็กต์ PendingIntent ที่เปลี่ยนแปลงได้ เราขอแนะนำอย่างยิ่งให้คุณใช้ Intent ที่ชัดเจนและกรอก ComponentName ด้วยวิธีนี้ เมื่อใดก็ตามที่แอปอื่นเรียกใช้ PendingIntent และส่งการควบคุมกลับไปยังแอปของคุณ คอมโพเนนต์เดียวกันในแอปของคุณจะเริ่มทำงานเสมอ
ใช้ Intent ที่ชัดเจนภายใน Intent รอดำเนินการ
หากต้องการกำหนดวิธีที่แอปอื่นๆ สามารถใช้ Intent รอดำเนินการของแอปคุณได้ดียิ่งขึ้น ให้รวม Intent รอดำเนินการไว้ใน Intent ที่ชัดเจนเสมอ โปรดทำตามขั้นตอนต่อไปนี้เพื่อช่วยให้ปฏิบัติตามแนวทางปฏิบัติแนะนำนี้
- ตรวจสอบว่ามีการตั้งค่าฟิลด์การดำเนินการ แพ็กเกจ และคอมโพเนนต์ของ Intent พื้นฐาน
-
ใช้
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 นั้นโดยเปรียบเทียบกับตัวกรอง 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 ก็ยังคงผ่าน ดังนั้น 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 ที่ระบุในตัวกรอง กฎมีดังนี้
- Intent ที่ไม่มีทั้ง URI และประเภท MIME จะผ่านการทดสอบก็ต่อเมื่อตัวกรองไม่ได้ระบุ URI หรือประเภท MIME ใดๆ
- Intent ที่มี URI แต่ไม่มีประเภท MIME (ไม่ว่าจะระบุอย่างชัดเจนหรืออนุมานได้จาก URI) จะผ่านการทดสอบก็ต่อเมื่อ URI ตรงกับรูปแบบ URI ของตัวกรอง และตัวกรองไม่ได้ระบุประเภท MIME เช่นกัน
- Intent ที่มีประเภท MIME แต่ไม่มี URI จะผ่านการทดสอบก็ต่อเมื่อตัวกรองแสดงประเภท MIME เดียวกันและไม่ได้ระบุรูปแบบ URI
- Intent ที่มีทั้ง URI และประเภท MIME (ไม่ว่าจะระบุอย่างชัดเจนหรืออนุมานได้จาก URI) จะผ่านส่วนประเภท MIME ของการทดสอบก็ต่อเมื่อประเภทนั้นตรงกับประเภทที่ระบุไว้ในตัวกรอง โดยจะผ่านการทดสอบส่วน URI
หาก URI ตรงกับ URI ในตัวกรอง หรือหากมี URI
content:หรือfile:และตัวกรองไม่ได้ระบุ URI กล่าวคือ ระบบจะถือว่าคอมโพเนนต์รองรับข้อมูลcontent:และfile:หากตัวกรองแสดงเฉพาะประเภท MIME
หมายเหตุ: หาก Intent ระบุ URI หรือประเภท MIME การทดสอบข้อมูลจะล้มเหลวหากไม่มีองค์ประกอบ <data> ใน <intent-filter>
กฎข้อสุดท้ายนี้ (กฎ (ง)) แสดงถึงความคาดหวังว่าคอมโพเนนต์จะสามารถรับข้อมูลในเครื่องจากไฟล์หรือ Content Provider ได้
ดังนั้น ตัวกรองจึงแสดงเฉพาะประเภทข้อมูลได้ และไม่จำเป็นต้องระบุชื่อรูปแบบ content: และ file: อย่างชัดเจน
ตัวอย่างต่อไปนี้แสดงกรณีทั่วไปที่องค์ประกอบ <data> บอก Android ว่าคอมโพเนนต์สามารถรับข้อมูลรูปภาพจาก Content Provider และแสดงข้อมูลดังกล่าวได้
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
ตัวกรองที่ระบุประเภทข้อมูลแต่ไม่ระบุ URI อาจเป็นตัวกรองที่พบบ่อยที่สุด เนื่องจากข้อมูลที่มีอยู่ส่วนใหญ่จะได้รับการจ่ายโดย Content Provider
การกำหนดค่าที่พบบ่อยอีกอย่างหนึ่งคือตัวกรองที่มีรูปแบบและประเภทข้อมูล ตัวอย่างเช่น องค์ประกอบ <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