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

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

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

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

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

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

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

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

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

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

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

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

ประเภท Intent

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

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

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

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

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

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

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

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

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

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

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

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

คอมโพเนนต์แอปควรประกาศตัวกรองแยกกันสำหรับงานแต่ละอย่างที่ทำได้โดยเฉพาะ ตัวอย่างเช่น กิจกรรมหนึ่งในแอปแกลเลอรีรูปภาพอาจมีตัวกรอง 2 ตัว ได้แก่ ตัวกรองตัวหนึ่งสำหรับดูรูปภาพ และตัวกรองอีกตัวหนึ่งสำหรับแก้ไขรูปภาพ เมื่อกิจกรรมเริ่มต้นขึ้น Intent จะตรวจสอบ 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 จะจํากัดคอมโพเนนต์ให้ตอบสนองต่อ Implicit Intent บางประเภทเท่านั้น แต่แอปอื่นอาจเริ่มคอมโพเนนต์แอปของคุณได้โดยใช้ Explicit Intent หากนักพัฒนาแอประบุชื่อคอมโพเนนต์ หากต้องการให้เฉพาะแอปของคุณเองเท่านั้นที่เริ่มคอมโพเนนต์ใดคอมโพเนนต์หนึ่งได้ ให้อย่าประกาศตัวกรอง Intent ในไฟล์ Manifest แต่ให้ตั้งค่าแอตทริบิวต์ exported เป็น "false" สำหรับคอมโพเนนต์นั้นแทน

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

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

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

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

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

หมายเหตุ: ประเภท 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 โดยเรียกใช้เมธอดการสร้างที่เกี่ยวข้อง ดังนี้

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

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

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

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

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

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

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

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

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

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

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

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

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

หากต้องการระบุหมวดหมู่ Intent ที่ยอมรับ ตัวกรอง Intent สามารถประกาศองค์ประกอบ <category> อย่างน้อย 1 รายการ ดังที่แสดงในตัวอย่างต่อไปนี้

<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> อย่างน้อย 1 รายการ ดังที่แสดงในตัวอย่างต่อไปนี้

<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 ว่าคอมโพเนนต์สามารถรับข้อมูลรูปภาพจากผู้ให้บริการเนื้อหาและแสดงข้อมูลดังกล่าว

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

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

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

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

การจับคู่ Intent

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

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