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
ตั้งชื่อคอมโพเนนต์กิจกรรมที่เฉพาะเจาะจงอย่างชัดเจน ระบบจะเริ่มคอมโพเนนต์นั้นทันที
เมื่อคุณใช้ 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:
URIcontent:
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 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 เพื่อให้ผู้ใช้เลือกแอปที่จะใช้
ดูข้อมูลเพิ่มเติมเกี่ยวกับการเปิดแอปอื่นๆ ได้ในคู่มือเกี่ยวกับการส่งผู้ใช้ไปยังแอปอื่น
การบังคับใช้เครื่องมือเลือกแอป
เมื่อมีแอปที่ตอบสนองต่อความตั้งใจโดยนัยมากกว่า 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
- แอปของคุณแยกแพ็กเกจ Intent ที่ฝังอยู่ออกจากส่วนเพิ่มเติมของ Intent ที่ส่ง
- แอปจะเริ่มคอมโพเนนต์แอปทันทีโดยใช้ 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 แสดงวิธีที่ระบบส่งการควบคุมจากแอป (ไคลเอ็นต์) ไปยังแอป (บริการ) อื่น และกลับไปที่แอปของคุณ
- แอปของคุณสร้าง Intent ที่เรียกใช้กิจกรรมในแอปอื่น ภายใน Intent นั้น คุณจะเพิ่มออบเจ็กต์
PendingIntent
เป็นส่วนเสริม PendingIntent นี้เรียกใช้คอมโพเนนต์ในแอปของคุณ ซึ่งไม่ได้ส่งออกคอมโพเนนต์นี้ - เมื่อได้รับ 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 แต่ละรายการจะกำหนดโดยองค์ประกอบ <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
โดยเรียกใช้เมธอดการสร้างที่เกี่ยวข้อง ดังนี้
PendingIntent.getActivity()
สำหรับIntent
ที่เริ่มต้นActivity
PendingIntent.getService()
สำหรับIntent
ที่เริ่มต้นService
PendingIntent.getBroadcast()
สำหรับIntent
ที่เริ่มต้นBroadcastReceiver
เว้นแต่ว่าแอปจะรับ 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 ที่ชัดเจนเสมอ โปรดทําตามขั้นตอนต่อไปนี้เพื่อช่วยปฏิบัติตามแนวทางปฏิบัติแนะนํานี้
- ตรวจสอบว่ามีการตั้งค่าฟิลด์การดำเนินการ แพ็กเกจ และคอมโพเนนต์ของ Intent พื้นฐาน
-
ใช้
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 ที่ระบุไว้ในตัวกรอง กฎมีดังนี้
- 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:
และ 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