Intent
คือออบเจ็กต์การรับส่งข้อความที่คุณสามารถใช้เพื่อส่งคําขอดำเนินการจากคอมโพเนนต์แอปอื่น
แม้ว่า Intent จะอำนวยความสะดวกในการสื่อสารระหว่างคอมโพเนนต์ได้หลายวิธี แต่ก็มีกรณีการใช้งานพื้นฐาน 3 กรณี ดังนี้
- การเริ่มกิจกรรม
Activity
แสดงหน้าจอเดียวในแอป คุณเริ่มอินสแตนซ์ใหม่ของActivity
ได้โดยการส่งIntent
ไปยังstartActivity()
Intent
อธิบายกิจกรรมที่จะเริ่มต้นและนําข้อมูลที่จําเป็นหากต้องการรับผลลัพธ์จากกิจกรรมเมื่อดำเนินการเสร็จแล้ว ให้โทรหา
startActivityForResult()
กิจกรรมของคุณจะได้รับผลลัพธ์เป็นออบเจ็กต์Intent
แยกต่างหากใน CallbackonActivityResult()
ของกิจกรรม ดูข้อมูลเพิ่มเติมได้ที่คู่มือกิจกรรม - การเริ่มบริการ
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 ที่ชัดเจนเท่านั้น
ข้อควรระวัง: โปรดใช้ความตั้งใจที่ชัดเจนเสมอเมื่อเริ่มต้น 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:
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
- แฟล็กได้รับการกำหนดไว้ในคลาส
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()
ด้วยเจตนาดังกล่าว ผู้ใช้จะเลือกแอปที่จะใช้แชร์เนื้อหาได้
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
เป็นส่วนเสริม Intent ที่รอดำเนินการนี้จะเรียกใช้ คอมโพเนนต์ในแอป โดยจะไม่ส่งออกคอมโพเนนต์นี้ - เมื่อได้รับ Intent ของแอป แอปนั้นจะแยกออบเจ็กต์
PendingIntent
ที่ฝังอยู่ - แอปอื่นเรียกใช้เมธอด
send()
ในออบเจ็กต์PendingIntent
- หลังจากส่งการควบคุมกลับไปที่แอปแล้ว ระบบจะเรียกใช้ 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
โดยเรียกใช้เมธอดการสร้างที่เกี่ยวข้อง ดังนี้
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
ที่ระบุเปลี่ยนแปลงหรือเปลี่ยนแปลงไม่ได้ ให้ใช้แฟล็ก 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 ที่ชัดเจนเสมอ เพื่อช่วยทำตามแนวทางปฏิบัติแนะนำดังกล่าว โปรดทำตามขั้นตอนต่อไปนี้
- ตรวจสอบว่ามีการตั้งค่าฟิลด์การดำเนินการ แพ็กเกจ และคอมโพเนนต์ของ Intent พื้นฐาน
-
ใช้
FLAG_IMMUTABLE
ซึ่งเพิ่มเข้ามาใน Android 6.0 (API ระดับ 23) เพื่อสร้าง Intent ที่รอดำเนินการ แฟล็กนี้ป้องกันไม่ให้แอปที่ได้รับPendingIntent
เติมพร็อพเพอร์ตี้ที่ไม่ได้สร้างไว้ หากminSdkVersion
ของแอปเท่ากับ22
หรือต่ำกว่า คุณระบุความปลอดภัยและความเข้ากันได้ร่วมกันได้โดยใช้โค้ดต่อไปนี้if (Build.VERSION.SDK_INT >= 23) { // Create a PendingIntent using FLAG_IMMUTABLE. } else { // Existing code that creates a PendingIntent. }
การแก้ไข Intent
เมื่อระบบได้รับ Intent ที่ไม่ชัดแจ้งเพื่อเริ่มกิจกรรม ระบบจะค้นหากิจกรรมที่เหมาะกับ Intent นั้นที่สุดโดยเปรียบเทียบกับตัวกรอง Intent ตาม 3 ด้านต่อไปนี้
- เริ่มถ่ายเลย
- ข้อมูล (ทั้ง URI และประเภทข้อมูล)
- หมวดหมู่
ส่วนต่อไปนี้อธิบายวิธีจับคู่ Intent กับคอมโพเนนต์ที่เหมาะสมตามการประกาศตัวกรอง Intent ในไฟล์ Manifest ของแอป
การทดสอบการดำเนินการ
หากต้องการระบุการดำเนินการของ Intent ที่ยอมรับ ตัวกรอง Intent สามารถประกาศองค์ประกอบ <action>
อย่างน้อย 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 ที่ระบุไว้ในตัวกรอง โดยมีกฎดังนี้
- 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()
จะแสดงรายการบริการที่คล้ายกัน
ทั้ง 2 วิธีการไม่ได้เปิดใช้งานคอมโพเนนต์ เพียงแต่แสดงวิธีการที่ตอบสนองได้ มีเมธอดที่คล้ายกัน
queryBroadcastReceivers()
สำหรับ Broadcast Receiver