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

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

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

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

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

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

    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 ที่ชัดเจนเท่านั้น

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

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

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

การรับ 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 จะจํากัดคอมโพเนนต์ให้ตอบสนองต่อ 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 1 ใน 2 รายการนี้ได้เช่นกัน

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

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

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

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

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

  • Intent ที่ส่งไปยังคอมโพเนนต์ที่ไม่ได้ประกาศตัวกรอง Intent
  • Intent ที่มาจากภายในแอปเดียวกัน
  • Intent ที่มาจากระบบ กล่าวคือ Intent ที่ส่งจาก "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 ที่ระบุเปลี่ยนแปลงหรือเปลี่ยนแปลงไม่ได้ ให้ใช้แฟล็ก 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 เพื่อปรับผลลัพธ์ของการเรียกใช้ความตั้งใจไม่ได้

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 ที่รอดำเนินการ แฟล็กนี้ป้องกันไม่ให้แอปที่ได้รับ 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> อย่างน้อย 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> อย่างน้อย 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> แต่มีทรัพยากร Dependency แบบเชิงเส้น

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

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

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

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

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

  1. Intent ที่ไม่มี URI หรือประเภท MIME จะผ่านการทดสอบก็ต่อเมื่อตัวกรองไม่ได้ระบุประเภท URI หรือ MIME ไว้
  2. Intent ที่มี URI แต่ไม่มีประเภท MIME (ไม่ได้ระบุไว้อย่างชัดเจนหรืออนุมานจาก URI) จะผ่านการทดสอบก็ต่อเมื่อ URI ตรงกับรูปแบบ URI ของตัวกรอง และตัวกรองไม่ได้ระบุประเภท MIME ด้วยเช่นกัน
  3. Intent ที่มีประเภท MIME แต่ไม่มี URI จะผ่านการทดสอบก็ต่อเมื่อตัวกรองแสดงประเภท MIME เดียวกันและไม่ระบุรูปแบบ URI
  4. Intent ที่มีทั้ง URI และประเภท MIME (ไม่ว่าจะระบุไว้อย่างชัดเจนหรืออนุมานได้จาก URI) จะผ่านส่วนประเภท MIME ของการทดสอบก็ต่อเมื่อประเภทนั้นตรงกับประเภทที่แสดงในตัวกรองเท่านั้น รายการจะผ่านส่วน URI ของการตรวจสอบไม่ว่าจะมี URI ที่ตรงกับ URI ในตัวกรอง หรือมี 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() จะแสดงรายการบริการที่คล้ายกัน ทั้ง 2 วิธีการไม่ได้เปิดใช้งานคอมโพเนนต์ เพียงแต่แสดงวิธีการที่ตอบสนองได้ มีเมธอดที่คล้ายกัน queryBroadcastReceivers() สำหรับ Broadcast Receiver