แอป Android ทุกแอปจะทำงานในแซนด์บ็อกซ์ที่มีสิทธิ์เข้าถึงแบบจำกัด หากแอปต้องใช้ทรัพยากรหรือข้อมูลภายนอกแซนด์บ็อกซ์ของตัวเอง คุณสามารถประกาศสิทธิ์รันไทม์และตั้งค่าคำขอสิทธิ์ที่ให้สิทธิ์เข้าถึงนี้ได้ ขั้นตอนเหล่านี้เป็นส่วนหนึ่งของเวิร์กโฟลว์สำหรับการใช้สิทธิ์
หากประกาศสิทธิ์ที่เป็นอันตรายและหากติดตั้งแอปในอุปกรณ์ที่ใช้ Android 6.0 (API ระดับ 23) ขึ้นไป คุณต้องขอสิทธิ์ที่เป็นอันตรายในรันไทม์โดยทำตามขั้นตอนในคู่มือนี้
หากคุณไม่ได้ประกาศสิทธิ์ที่เป็นอันตราย หรือหากแอปของคุณติดตั้งอยู่ใน อุปกรณ์ที่ใช้ Android 5.1 (API ระดับ 22) หรือต่ำกว่า ระบบจะให้สิทธิ์โดยอัตโนมัติ และคุณไม่จำเป็นต้องทำขั้นตอนที่เหลือในหน้านี้
หลักการพื้นฐาน
หลักการพื้นฐานในการขอสิทธิ์ในระหว่างรันไทม์มีดังนี้
- ขอสิทธิ์ในบริบทเมื่อผู้ใช้เริ่มโต้ตอบกับ ฟีเจอร์ที่ต้องใช้สิทธิ์
- อย่าบล็อกผู้ใช้ ให้ตัวเลือกในการยกเลิกโฟลว์ UI เพื่อการศึกษาเสมอ เช่น โฟลว์ที่อธิบายเหตุผลในการขอสิทธิ์
- หากผู้ใช้ปฏิเสธหรือเพิกถอนสิทธิ์ที่ฟีเจอร์หนึ่งต้องใช้ ให้ลดแอปลงอย่างค่อยเป็นค่อยไปเพื่อให้ผู้ใช้ใช้แอปของคุณต่อไปได้ ซึ่งอาจทำได้โดยการปิดใช้ฟีเจอร์ที่ต้องใช้สิทธิ์
- อย่าคาดเดาพฤติกรรมของระบบ เช่น อย่าคิดว่าสิทธิ์จะปรากฏในกลุ่มสิทธิ์เดียวกัน กลุ่มสิทธิ์ เพียงช่วยให้ระบบลดจำนวนกล่องโต้ตอบของระบบที่ แสดงต่อผู้ใช้เมื่อแอปขอสิทธิ์ที่เกี่ยวข้องอย่างใกล้ชิด
เวิร์กโฟลว์สำหรับการขอสิทธิ์
ก่อนที่จะประกาศและขอสิทธิ์รันไทม์ในแอป ให้ประเมิน ว่าแอปของคุณจำเป็นต้องทำเช่นนั้นหรือไม่ คุณสามารถดำเนินการตาม Use Case หลายอย่างในแอป เช่น ถ่ายรูป หยุดการเล่นสื่อชั่วคราว และแสดงโฆษณาที่เกี่ยวข้อง โดยไม่ต้องประกาศสิทธิ์ใดๆ
หากสรุปว่าแอปของคุณต้องประกาศและขอสิทธิ์รันไทม์ ให้ทำตามขั้นตอนต่อไปนี้
- ประกาศสิทธิ์ที่แอปอาจต้องขอในไฟล์ Manifest ของแอป
- ออกแบบประสบการณ์ของผู้ใช้ (UX) ของแอปเพื่อให้การดำเนินการที่เฉพาะเจาะจงในแอป เชื่อมโยงกับสิทธิ์รันไทม์ที่เฉพาะเจาะจง แจ้งให้ผู้ใช้ทราบว่าการดำเนินการใดบ้างที่อาจกำหนดให้ผู้ใช้ให้สิทธิ์แอปของคุณเข้าถึงข้อมูลส่วนตัวของผู้ใช้
- รอให้ผู้ใช้เรียกใช้ฟังก์ชันหรืองานในแอป ของคุณที่ต้องเข้าถึงข้อมูลส่วนตัวของผู้ใช้ที่เฉพาะเจาะจง ในเวลานั้น แอปของคุณจะขอสิทธิ์รันไทม์ที่จำเป็นสำหรับการเข้าถึงข้อมูลดังกล่าวได้
- ตรวจสอบว่าผู้ใช้ได้ให้ สิทธิ์รันไทม์ที่แอปของคุณต้องการแล้วหรือยัง หากเป็นเช่นนั้น แอปของคุณจะเข้าถึงข้อมูลผู้ใช้ส่วนตัวได้ หากไม่ ให้ลองทำขั้นตอนถัดไป - คุณต้องตรวจสอบว่ามีสิทธิ์ทุกครั้งที่ดำเนินการซึ่งต้องใช้สิทธิ์นั้น 
- ตรวจสอบว่าแอปควรแสดงเหตุผลแก่ผู้ใช้หรือไม่ โดยอธิบายว่าทำไมแอปของคุณจึงต้องการให้ผู้ใช้ให้สิทธิ์รันไทม์ ที่เฉพาะเจาะจง หากระบบพิจารณาว่าแอปของคุณไม่ควรแสดงเหตุผล ให้ทำตามขั้นตอนถัดไปโดยตรงโดยไม่ต้องแสดงองค์ประกอบ UI - อย่างไรก็ตาม หากระบบพิจารณาว่าแอปของคุณควรแสดงเหตุผล ให้แสดงเหตุผลแก่ผู้ใช้ในองค์ประกอบ UI ในเหตุผลนี้ ให้อธิบายอย่างชัดเจนว่าแอปของคุณพยายามเข้าถึงข้อมูลใดและแอป จะมอบประโยชน์ใดแก่ผู้ใช้ได้หากผู้ใช้ให้สิทธิ์รันไทม์ หลังจากที่ ผู้ใช้รับทราบเหตุผลแล้ว ให้ไปยังขั้นตอนถัดไป 
- ขอสิทธิ์รันไทม์ที่แอปของคุณต้องใช้ เพื่อเข้าถึงข้อมูลส่วนตัวของผู้ใช้ ระบบจะแสดงข้อความแจ้งสิทธิ์รันไทม์ เช่น ข้อความที่แสดงในหน้าภาพรวมของสิทธิ์ 
- ตรวจสอบคำตอบของผู้ใช้ ไม่ว่าผู้ใช้จะเลือกให้หรือปฏิเสธ สิทธิ์รันไทม์ 
- หากผู้ใช้ให้สิทธิ์แก่แอป คุณจะเข้าถึงข้อมูลส่วนตัวของผู้ใช้ได้ หากผู้ใช้ปฏิเสธที่จะให้สิทธิ์ ให้ลดระดับประสบการณ์การใช้งานแอปอย่างเหมาะสมเพื่อให้แอปมี ฟังก์ชันการทํางานสําหรับผู้ใช้โดยไม่ต้องใช้ข้อมูลที่ได้รับการปกป้องโดยสิทธิ์ดังกล่าว 
รูปที่ 1 แสดงเวิร์กโฟลว์และชุดการตัดสินใจที่เกี่ยวข้องกับกระบวนการนี้
พิจารณาว่าแอปได้รับสิทธิ์แล้วหรือไม่
หากต้องการตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์เฉพาะแก่แอปของคุณแล้วหรือไม่ ให้ส่งสิทธิ์นั้นไปยังเมธอด [ContextCompat.checkSelfPermission()][12]
เมธอดนี้จะแสดงผลเป็น PERMISSION_GRANTED หรือ
PERMISSION_DENIED โดยขึ้นอยู่กับว่าแอปของคุณมีสิทธิ์หรือไม่
อธิบายเหตุผลที่แอปของคุณต้องการสิทธิ์
กล่องโต้ตอบสิทธิ์ที่ระบบแสดงเมื่อคุณเรียกใช้
[requestPermissions()][15] จะบอกว่าแอปต้องการสิทธิ์ใด แต่ไม่ได้
บอกเหตุผล ในบางกรณี ผู้ใช้อาจรู้สึกงุนงง ขอแนะนำให้
อธิบายให้ผู้ใช้ทราบว่าทำไมแอปของคุณจึงต้องการสิทธิ์ก่อนที่จะเรียกใช้
requestPermissions()
งานวิจัยแสดงให้เห็นว่าผู้ใช้จะรู้สึกสบายใจมากขึ้นเมื่อได้รับคำขอสิทธิ์ หากทราบว่าทำไมแอปจึงต้องขอสิทธิ์ดังกล่าว เช่น สิทธิ์นั้นจำเป็นต่อการรองรับฟีเจอร์หลักของแอปหรือเพื่อการโฆษณาหรือไม่ ดังนั้น หากคุณใช้การเรียก API เพียงบางส่วนที่อยู่ภายใต้กลุ่มสิทธิ์ ให้ระบุอย่างชัดเจนว่าคุณใช้สิทธิ์ใดและเพราะเหตุใด เช่น หากคุณใช้เฉพาะตำแหน่งแบบหยาบ ให้แจ้งให้ผู้ใช้ทราบในคำอธิบายแอปหรือในบทความช่วยเหลือเกี่ยวกับแอป
ในบางกรณี การแจ้งให้ผู้ใช้ทราบเกี่ยวกับการเข้าถึงข้อมูลที่ละเอียดอ่อนแบบเรียลไทม์ก็เป็นประโยชน์เช่นกัน ตัวอย่างเช่น หากคุณเข้าถึงกล้องหรือไมโครโฟน ให้แจ้งให้ผู้ใช้ทราบโดยใช้ไอคอนการแจ้งเตือนที่ใดที่หนึ่งในแอป หรือในถาดการแจ้งเตือน (หากแอปพลิเคชันทํางานในเบื้องหลัง) เพื่อไม่ให้ดูเหมือนว่าคุณกําลังรวบรวมข้อมูลอย่างลับๆ
หากคุณต้องขอสิทธิ์เพื่อให้ฟีเจอร์ทำงานได้ แต่ผู้ใช้ไม่เข้าใจเหตุผล โปรดหาวิธีอธิบายเหตุผลที่คุณต้องใช้สิทธิ์ที่ละเอียดอ่อน
หากเมธอด ContextCompat.checkSelfPermission() แสดงผลเป็น PERMISSION_DENIED
ให้โทรหา [shouldShowRequestPermissionRationale()][16] หากเมธอดนี้แสดงผลเป็น
true ให้แสดง UI ที่ให้ความรู้แก่ผู้ใช้ ใน UI นี้ ให้อธิบายว่าทำไมฟีเจอร์ที่ผู้ใช้ต้องการเปิดใช้จึงต้องมีสิทธิ์เฉพาะ
นอกจากนี้ หากแอปขอสิทธิ์ที่เกี่ยวข้องกับตำแหน่ง ไมโครโฟน หรือกล้อง โปรดอธิบายเหตุผลที่แอปต้องเข้าถึงข้อมูลนี้
ขอสิทธิ์
หลังจากที่ผู้ใช้ดู UI เพื่อการศึกษา หรือค่าที่ส่งคืนของ
shouldShowRequestPermissionRationale() ระบุว่าคุณไม่จำเป็นต้องแสดง
UI เพื่อการศึกษา ให้ขอสิทธิ์ ผู้ใช้จะเห็นกล่องโต้ตอบสิทธิ์ของระบบ
ซึ่งสามารถเลือกได้ว่าจะให้สิทธิ์ใดสิทธิ์หนึ่งแก่แอป
ของคุณหรือไม่
โดยให้ใช้สัญญา RequestPermission ซึ่งรวมอยู่ในไลบรารี AndroidX
 ที่คุณอนุญาตให้ระบบจัดการรหัสคำขอสิทธิ์
ให้คุณ เนื่องจากการใช้สัญญา RequestPermission จะช่วยลดความซับซ้อนของตรรกะ
จึงเป็นโซลูชันที่แนะนำเมื่อเป็นไปได้ อย่างไรก็ตาม หากจำเป็น คุณก็จัดการรหัสคำขอด้วยตนเองได้เช่นกัน ซึ่งเป็นส่วนหนึ่งของคำขอสิทธิ์ และ
รวมรหัสคำขอนี้ไว้ในตรรกะการเรียกกลับของสิทธิ์
อนุญาตให้ระบบจัดการรหัสคำขอสิทธิ์
หากต้องการอนุญาตให้ระบบจัดการรหัสคำขอที่เชื่อมโยงกับคำขอสิทธิ์ ให้เพิ่มทรัพยากร Dependency ในไลบรารีต่อไปนี้ในไฟล์ build.gradle ของโมดูล
- androidx.activityเวอร์ชัน 1.2.0 ขึ้นไป
- androidx.fragmentเวอร์ชัน 1.3.0 ขึ้นไป
จากนั้นคุณจะใช้คลาสใดคลาสหนึ่งต่อไปนี้ได้
- หากต้องการขอสิทธิ์เดียว ให้ใช้ RequestPermission
- หากต้องการขอสิทธิ์หลายรายการพร้อมกัน ให้ใช้
RequestMultiplePermissions
ขั้นตอนต่อไปนี้แสดงวิธีใช้RequestPermissionสัญญา โดยกระบวนการนี้จะเกือบเหมือนกับRequestMultiplePermissionsสัญญา
- ในตรรกะการเริ่มต้นของกิจกรรมหรือ Fragment ให้ส่งการใช้งาน - ActivityResultCallbackไปยังการเรียก- registerForActivityResult()- ActivityResultCallbackจะกำหนด วิธีที่แอปจัดการการตอบกลับคำขอสิทธิ์ของผู้ใช้- เก็บการอ้างอิงไปยังค่าที่ฟังก์ชัน - registerForActivityResult()แสดงผล ซึ่งมีประเภทเป็น- ActivityResultLauncher
- หากต้องการแสดงกล่องโต้ตอบสิทธิ์ของระบบเมื่อจำเป็น ให้เรียกใช้เมธอด - launch()ในอินสแตนซ์ของ- ActivityResultLauncherที่ คุณบันทึกไว้ในขั้นตอนก่อนหน้า- หลังจากเรียกใช้ - launch()แล้ว กล่องโต้ตอบสิทธิ์ของระบบจะปรากฏขึ้น เมื่อผู้ใช้เลือก ระบบจะเรียกใช้การติดตั้งใช้งาน- ActivityResultCallbackแบบไม่พร้อมกัน ซึ่งคุณกำหนดไว้ในขั้นตอนก่อนหน้า- หมายเหตุ: แอปของคุณปรับแต่งกล่องโต้ตอบที่ปรากฏ เมื่อคุณโทรหา - launch()ไม่ได้ หากต้องการให้ข้อมูลหรือบริบทเพิ่มเติมแก่ผู้ใช้ ให้เปลี่ยน UI ของแอปเพื่อให้ผู้ใช้เข้าใจได้ง่ายขึ้นว่าทำไมฟีเจอร์ในแอปจึงต้องใช้สิทธิ์ใดสิทธิ์หนึ่ง เช่น คุณอาจเปลี่ยนข้อความในปุ่มที่เปิดใช้ฟีเจอร์ ได้- นอกจากนี้ ข้อความในกล่องโต้ตอบสิทธิ์ของระบบยังอ้างอิงถึงกลุ่ม สิทธิ์ที่เชื่อมโยงกับสิทธิ์ที่คุณขอ การจัดกลุ่มสิทธิ์นี้ออกแบบมาเพื่อให้ระบบใช้งานง่าย และแอปของคุณไม่ควรอาศัยการมีสิทธิ์ภายในหรือภายนอกกลุ่มสิทธิ์ที่เฉพาะเจาะจง 
ข้อมูลโค้ดต่อไปนี้แสดงวิธีจัดการการตอบกลับการให้สิทธิ์
Kotlin
when { ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION ) == PackageManager.PERMISSION_GRANTED -> { // You can use the API that requires the permission. performAction(...) } ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.REQUESTED_PERMISSION) -> { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...) } else -> { // You can directly ask for the permission. requestPermissions(CONTEXT, arrayOf(Manifest.permission.REQUESTED_PERMISSION), REQUEST_CODE) } }
Java
if (ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION) == PackageManager.PERMISSION_GRANTED) { // You can use the API that requires the permission. performAction(...); } else if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.REQUESTED_PERMISSION)) { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...); } else { // You can directly ask for the permission. requestPermissions(CONTEXT, new String[] { Manifest.permission.REQUESTED_PERMISSION }, REQUEST_CODE); }
และตัวอย่างโค้ดนี้แสดงกระบวนการที่แนะนําในการตรวจสอบ สิทธิ์และขอสิทธิ์จากผู้ใช้เมื่อจําเป็น
Kotlin
when { ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION ) == PackageManager.PERMISSION_GRANTED -> { // You can use the API that requires the permission. } ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.REQUESTED_PERMISSION) -> { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...) } else -> { // You can directly ask for the permission. // The registered ActivityResultCallback gets the result of this request. requestPermissionLauncher.launch( Manifest.permission.REQUESTED_PERMISSION) } }
Java
if (ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION) == PackageManager.PERMISSION_GRANTED) { // You can use the API that requires the permission. performAction(...); } else if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.REQUESTED_PERMISSION)) { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...); } else { // You can directly ask for the permission. // The registered ActivityResultCallback gets the result of this request. requestPermissionLauncher.launch( Manifest.permission.REQUESTED_PERMISSION); }
จัดการโค้ดคำขอสิทธิ์ด้วยตนเอง
คุณสามารถจัดการโค้ดคำขอสิทธิ์ด้วยตนเองได้แทนการอนุญาตให้ระบบจัดการโค้ด
คำขอสิทธิ์ โดยให้ใส่รหัสคำขอในการเรียกใช้ [requestPermissions()][15]
ข้อมูลโค้ดต่อไปนี้แสดงวิธีขอสิทธิ์โดยใช้รหัสคำขอ
Kotlin
when { ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION ) == PackageManager.PERMISSION_GRANTED -> { // You can use the API that requires the permission. performAction(...) } ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.REQUESTED_PERMISSION) -> { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...) } else -> { // You can directly ask for the permission. requestPermissions(CONTEXT, arrayOf(Manifest.permission.REQUESTED_PERMISSION), REQUEST_CODE) } }
Java
if (ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION) == PackageManager.PERMISSION_GRANTED) { // You can use the API that requires the permission. performAction(...); } else if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.REQUESTED_PERMISSION)) { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...); } else { // You can directly ask for the permission. requestPermissions(CONTEXT, new String[] { Manifest.permission.REQUESTED_PERMISSION }, REQUEST_CODE); }
หลังจากที่ผู้ใช้ตอบกลับกล่องโต้ตอบสิทธิ์ของระบบแล้ว ระบบจะเรียกใช้การติดตั้งใช้งาน onRequestPermissionsResult() ของแอป ระบบจะส่งคำตอบของผู้ใช้ไปยังกล่องโต้ตอบสิทธิ์ รวมถึงรหัสคำขอที่คุณกำหนด ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
Kotlin
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { when (requestCode) { PERMISSION_REQUEST_CODE -> { // If request is cancelled, the result arrays are empty. if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // Permission is granted. Continue the action or workflow // in your app. } else { // Explain to the user that the feature is unavailable because // the feature requires a permission that the user has denied. // At the same time, respect the user's decision. Don't link to // system settings in an effort to convince the user to change // their decision. } return } // Add other 'when' lines to check for other // permissions this app might request. else -> { // Ignore all other requests. } } }
Java
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case PERMISSION_REQUEST_CODE: // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission is granted. Continue the action or workflow // in your app. } else { // Explain to the user that the feature is unavailable because // the feature requires a permission that the user has denied. // At the same time, respect the user's decision. Don't link to // system settings in an effort to convince the user to change // their decision. } return; } // Other 'case' lines to check for other // permissions this app might request. } }
ขอสิทธิ์เข้าถึงตำแหน่ง
เมื่อขอสิทธิ์เข้าถึงตำแหน่ง ให้ทำตามแนวทางปฏิบัติแนะนำเดียวกันกับสิทธิ์รันไทม์อื่นๆ ความแตกต่างที่สำคัญอย่างหนึ่งเมื่อพูดถึงสิทธิ์เข้าถึงตำแหน่งคือระบบมีสิทธิ์หลายอย่างที่เกี่ยวข้องกับตำแหน่ง สิทธิ์ที่คุณขอและวิธีขอจะขึ้นอยู่กับ ข้อกำหนดด้านตำแหน่งสำหรับ Use Case ของแอป
ตำแหน่งที่ทำงานอยู่เบื้องหน้า
หากแอปของคุณมีฟีเจอร์ที่แชร์หรือรับข้อมูลตำแหน่งเพียงครั้งเดียวหรือเป็นระยะเวลาที่กำหนด ฟีเจอร์นั้นจะต้องมีสิทธิ์เข้าถึงตำแหน่งในเบื้องหน้า ตัวอย่างเช่น
- ฟีเจอร์ในแอปนำทางช่วยให้ผู้ใช้ได้รับเส้นทางแบบเลี้ยวต่อเลี้ยว
- ฟีเจอร์ในแอปรับส่งข้อความช่วยให้ผู้ใช้แชร์ตำแหน่งปัจจุบันกับผู้ใช้รายอื่นได้
ระบบจะถือว่าแอปของคุณใช้ตำแหน่งในเบื้องหน้าหากฟีเจอร์ของ แอปเข้าถึงตำแหน่งปัจจุบันของอุปกรณ์ในสถานการณ์ใดสถานการณ์หนึ่งต่อไปนี้
- กิจกรรมที่เป็นของแอปจะปรากฏ
- แอปของคุณกำลังเรียกใช้บริการที่ทำงานอยู่เบื้องหน้า เมื่อบริการที่ทำงานอยู่เบื้องหน้า ทำงานอยู่ ระบบจะเพิ่มการรับรู้ของผู้ใช้โดยแสดงการแจ้งเตือนแบบต่อเนื่อง แอปจะยังคงมีสิทธิ์เข้าถึงเมื่ออยู่ในเบื้องหลัง เช่น เมื่อผู้ใช้กดปุ่มหน้าแรกในอุปกรณ์หรือปิด จอแสดงผลของอุปกรณ์ - ใน Android 10 (API ระดับ 29) ขึ้นไป คุณต้องประกาศประเภทบริการที่ทำงานอยู่เบื้องหน้าของ - locationตามที่แสดงในข้อมูลโค้ดต่อไปนี้ ใน Android เวอร์ชันก่อนหน้า เราขอแนะนำให้คุณประกาศประเภทบริการที่ทำงานอยู่เบื้องหน้านี้- <!-- Recommended for Android 9 (API level 28) and lower. --> <!-- Required for Android 10 (API level 29) and higher. --> <service android:name="MyNavigationService" android:foregroundServiceType="location" ... > <!-- Any inner elements go here. --> </service> 
คุณประกาศความจำเป็นสำหรับตำแหน่งในเบื้องหน้าเมื่อแอปขอสิทธิ์ ACCESS_COARSE_LOCATION หรือสิทธิ์ ACCESS_FINE_LOCATION ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
<manifest ... > <!-- Include this permission any time your app needs location information. --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- Include only if your app benefits from precise location access. --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> </manifest>
ตำแหน่งในเบื้องหลัง
แอปต้องมีสิทธิ์เข้าถึงตำแหน่งในเบื้องหลังหากฟีเจอร์ภายในแอป แชร์ตำแหน่งกับผู้ใช้รายอื่นอย่างต่อเนื่องหรือใช้ Geofencing API ตัวอย่างบางส่วนมีดังนี้
- ภายในแอปการแชร์ตำแหน่งในครอบครัว ฟีเจอร์หนึ่งช่วยให้ผู้ใช้แชร์ตำแหน่งกับสมาชิกในครอบครัวได้อย่างต่อเนื่อง
- ภายในแอป IoT ฟีเจอร์หนึ่งจะช่วยให้ผู้ใช้กำหนดค่าอุปกรณ์สมาร์ทโฮมได้ เพื่อให้ปิดเมื่อผู้ใช้ออกจากบ้านและเปิดอีกครั้งเมื่อ ผู้ใช้กลับถึงบ้าน
ระบบจะถือว่าแอปของคุณใช้ตำแหน่งในเบื้องหลังหากแอปเข้าถึงตำแหน่งปัจจุบันของอุปกรณ์ในสถานการณ์อื่นๆ นอกเหนือจากที่อธิบายไว้ในส่วนตำแหน่งในเบื้องหน้า ความแม่นยำของตำแหน่งในเบื้องหลังจะเหมือนกับความแม่นยำของตำแหน่งในเบื้องหน้า ซึ่ง ขึ้นอยู่กับสิทธิ์เข้าถึงตำแหน่งที่แอปประกาศ
ใน Android 10 (API ระดับ 29) ขึ้นไป คุณต้องประกาศสิทธิ์ ACCESS_BACKGROUND_LOCATION ใน Manifest ของแอปเพื่อขอสิทธิ์เข้าถึงตำแหน่งในเบื้องหลังที่รันไทม์
 ใน Android เวอร์ชันก่อนหน้า เมื่อ
แอปได้รับสิทธิ์เข้าถึงตำแหน่งเมื่อทำงานอยู่เบื้องหน้า แอปจะได้รับสิทธิ์เข้าถึงตำแหน่งเมื่อทำงานอยู่เบื้องหลัง
โดยอัตโนมัติด้วย
<manifest ... >
  <!-- Required only when requesting background location access on
       Android 10 (API level 29) and higher. -->
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
จัดการการปฏิเสธสิทธิ์
หากผู้ใช้ปฏิเสธคำขอสิทธิ์ แอปของคุณต้องช่วยให้ผู้ใช้เข้าใจ ผลกระทบของการปฏิเสธสิทธิ์ โดยเฉพาะอย่างยิ่ง แอปของคุณต้องแจ้งให้ผู้ใช้ทราบถึงฟีเจอร์ที่ไม่ทำงานเนื่องจากไม่มีสิทธิ์ เมื่อดำเนินการดังกล่าว โปรดคำนึงถึงแนวทางปฏิบัติแนะนำต่อไปนี้
- ดึงดูดความสนใจของผู้ใช้ ไฮไลต์ส่วนที่เฉพาะเจาะจงของ UI ของแอป ซึ่งมีฟังก์ชันการทำงานที่จำกัดเนื่องจากแอปไม่มีสิทธิ์ที่จำเป็น ตัวอย่างสิ่งที่คุณทำได้มีดังนี้ - แสดงข้อความในตำแหน่งที่ผลลัพธ์หรือข้อมูลของฟีเจอร์ควรจะปรากฏ
- แสดงปุ่มอื่นที่มีไอคอนและสีของข้อผิดพลาด
 
- ใช้คำที่เฉพาะเจาะจง อย่าแสดงข้อความทั่วไป แต่ให้ระบุให้ชัดเจนว่าฟีเจอร์ใดบ้างที่ใช้งานไม่ได้เนื่องจากแอปของคุณไม่มีสิทธิ์ที่จำเป็น 
- อย่าบล็อกอินเทอร์เฟซผู้ใช้ กล่าวคือ อย่าแสดงข้อความเตือนแบบเต็มหน้าจอที่ป้องกันไม่ให้ผู้ใช้ใช้แอปของคุณต่อไป 
ในขณะเดียวกัน แอปของคุณต้องเคารพการตัดสินใจของผู้ใช้ในการปฏิเสธที่จะให้สิทธิ์ ตั้งแต่ Android 11 (API ระดับ 30) เป็นต้นไป หากผู้ใช้แตะปฏิเสธสำหรับ สิทธิ์หนึ่งๆ มากกว่า 1 ครั้งตลอดอายุการติดตั้งแอป ในอุปกรณ์หนึ่งๆ ผู้ใช้จะไม่เห็นกล่องโต้ตอบสิทธิ์ของระบบหากแอป ขอสิทธิ์นั้นอีกครั้ง การกระทำของผู้ใช้หมายความว่า "ไม่ต้องถามอีก" ในเวอร์ชันก่อนหน้า ผู้ใช้จะเห็นกล่องโต้ตอบสิทธิ์ของระบบทุกครั้งที่แอปของคุณ ขอสิทธิ์ เว้นแต่ผู้ใช้จะเลือกช่องทำเครื่องหมายหรือตัวเลือก"อย่าถามอีก" ไว้ก่อนหน้านี้
หากผู้ใช้ปฏิเสธคำขอสิทธิ์มากกว่า 1 ครั้ง ระบบจะถือว่าเป็นการปฏิเสธถาวร คุณควรขอสิทธิ์จากผู้ใช้เมื่อผู้ใช้ต้องการเข้าถึงฟีเจอร์ใดฟีเจอร์หนึ่งเท่านั้น ไม่เช่นนั้น คุณอาจเสียสิทธิ์ในการขอสิทธิ์อีกครั้งโดยไม่ตั้งใจ
ในบางสถานการณ์ ระบบอาจปฏิเสธสิทธิ์โดยอัตโนมัติโดยที่ผู้ใช้ไม่ต้องดำเนินการใดๆ (ระบบอาจให้สิทธิ์โดยอัตโนมัติด้วย) สิ่งสำคัญคืออย่าคาดเดาเกี่ยวกับลักษณะการทำงานอัตโนมัติ ทุกครั้งที่ แอปต้องเข้าถึงฟังก์ชันการทำงานที่ต้องใช้สิทธิ์ ให้ตรวจสอบว่า แอปยังคงได้รับสิทธิ์นั้นอยู่
ดูแนวทางปฏิบัติแนะนำเกี่ยวกับสิทธิ์ของแอปด้วย เพื่อมอบประสบการณ์การใช้งานที่ดีที่สุดแก่ผู้ใช้เมื่อขอสิทธิ์ของแอป
ตรวจสอบสถานะการปฏิเสธเมื่อทดสอบและแก้ไขข้อบกพร่อง
หากต้องการดูว่าแอปถูกปฏิเสธสิทธิ์อย่างถาวรหรือไม่ (เพื่อวัตถุประสงค์ในการแก้ไขข้อบกพร่องและการทดสอบ) ให้ใช้คำสั่งต่อไปนี้
adb shell dumpsys package PACKAGE_NAME
โดยที่ PACKAGE_NAME คือชื่อแพ็กเกจที่จะตรวจสอบ
เอาต์พุตของคำสั่งจะมีส่วนที่มีลักษณะดังนี้
... runtime permissions: android.permission.POST_NOTIFICATIONS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_SET|USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] android.permission.BLUETOOTH_CONNECT: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED] ...
USER_SET จะแจ้งสิทธิ์ที่ผู้ใช้ปฏิเสธไปแล้ว
สิทธิ์ที่ถูกปฏิเสธอย่างถาวรโดยการเลือกปฏิเสธ 2 ครั้งจะ
ถูกแจ้งโดย USER_FIXED
หากต้องการให้ผู้ทดสอบเห็นกล่องโต้ตอบคำขอระหว่างการทดสอบ ให้รีเซ็ตฟีเจอร์ นี้เมื่อดีบักแอปเสร็จแล้ว โดยใช้คำสั่งต่อไปนี้
adb shell pm clear-permission-flags PACKAGE_NAME PERMISSION_NAME user-set user-fixed
PERMISSION_NAME คือชื่อสิทธิ์ที่ต้องการ รีเซ็ต
PERMISSION_NAME คือชื่อสิทธิ์ที่ต้องการรีเซ็ต
หากต้องการดูรายการสิทธิ์ของแอป Android ทั้งหมด โปรดไปที่หน้าเอกสารอ้างอิง API ของสิทธิ์
สิทธิ์ครั้งเดียว
ตั้งแต่ Android 11 (API ระดับ 30) เป็นต้นไป เมื่อใดก็ตามที่แอปขอสิทธิ์ ที่เกี่ยวข้องกับตำแหน่ง ไมโครโฟน หรือกล้อง กล่องโต้ตอบสิทธิ์ที่แสดงต่อผู้ใช้ จะมีตัวเลือกที่ชื่อเฉพาะครั้งนี้ ดังที่แสดงในรูปที่ 2 หากผู้ใช้ เลือกตัวเลือกนี้ในกล่องโต้ตอบ แอปของคุณจะได้รับสิทธิ์แบบครั้งเดียว ชั่วคราว
จากนั้นแอปจะเข้าถึงข้อมูลที่เกี่ยวข้องได้เป็นระยะเวลาหนึ่งซึ่งขึ้นอยู่กับ พฤติกรรมของแอปและการกระทําของผู้ใช้ ดังนี้
- ขณะที่กิจกรรมของแอปปรากฏอยู่ แอปจะเข้าถึงข้อมูลได้
- หากผู้ใช้ส่งแอปของคุณไปไว้เบื้องหลัง แอปจะยังคง เข้าถึงข้อมูลได้ในช่วงเวลาสั้นๆ
- หากคุณเปิดใช้บริการที่ทำงานอยู่เบื้องหน้าขณะที่กิจกรรมแสดงอยู่ และผู้ใช้ย้ายแอปไปทำงานที่เบื้องหลัง แอปจะยังคงเข้าถึงข้อมูลได้จนกว่าบริการที่ทำงานอยู่เบื้องหน้าจะหยุดทำงาน
กระบวนการของแอปจะสิ้นสุดเมื่อมีการเพิกถอนสิทธิ์
หากผู้ใช้เพิกถอนสิทธิ์แบบครั้งเดียว เช่น ในการตั้งค่าระบบ แอปของคุณจะเข้าถึงข้อมูลไม่ได้ ไม่ว่าคุณจะเปิดใช้บริการที่ทำงานอยู่เบื้องหน้าหรือไม่ก็ตาม เช่นเดียวกับสิทธิ์อื่นๆ หากผู้ใช้เพิกถอนสิทธิ์แบบครั้งเดียวของแอป กระบวนการของแอปจะสิ้นสุดลง
เมื่อผู้ใช้เปิดแอปของคุณในครั้งถัดไปและฟีเจอร์ในแอปขอสิทธิ์เข้าถึง ตำแหน่ง ไมโครโฟน หรือกล้อง ระบบจะแจ้งให้ผู้ใช้ขอสิทธิ์อีกครั้ง
รีเซ็ตสิทธิ์ที่ไม่ได้ใช้
Android มีหลายวิธีในการรีเซ็ตสิทธิ์รันไทม์ที่ไม่ได้ใช้กลับไปเป็น สถานะเริ่มต้นที่ถูกปฏิเสธ
- API ที่คุณสามารถนำสิทธิ์เข้าถึงสิทธิ์รันไทม์ที่ไม่ได้ใช้ของแอปออกได้โดยไม่ต้องรอให้ระบบแจ้ง
- กลไกของระบบที่รีเซ็ตสิทธิ์ของแอปที่ไม่ได้ใช้โดยอัตโนมัติ
นำสิทธิ์เข้าถึงของแอปออก
ใน Android 13 (API ระดับ 33) ขึ้นไป คุณสามารถนำสิทธิ์เข้าถึง สิทธิ์รันไทม์ที่แอปไม่ต้องการใช้อีกต่อไปออกได้ เมื่ออัปเดตแอป ให้ทำตามขั้นตอนนี้เพื่อให้ผู้ใช้มีแนวโน้มที่จะเข้าใจมากขึ้นว่าทำไมแอปของคุณ จึงยังคงขอสิทธิ์ที่เฉพาะเจาะจง ความรู้ดังกล่าวจะช่วยสร้างความไว้วางใจของผู้ใช้ในแอปของคุณ
หากต้องการนำสิทธิ์เข้าถึงสิทธิ์รันไทม์ออก ให้ส่งชื่อของสิทธิ์นั้นไปยัง
revokeSelfPermissionOnKill() หากต้องการนำสิทธิ์เข้าถึงกลุ่มสิทธิ์รันไทม์ออกพร้อมกัน ให้ส่งคอลเล็กชันชื่อสิทธิ์ไปยัง revokeSelfPermissionsOnKill() กระบวนการนำสิทธิ์ออกจะเกิดขึ้น
แบบไม่พร้อมกันและจะปิดกระบวนการทั้งหมดที่เชื่อมโยงกับ UID ของแอป
หากต้องการให้ระบบนำสิทธิ์เข้าถึงของแอปออก คุณต้องปิดกระบวนการทั้งหมด ที่เชื่อมโยงกับแอป เมื่อคุณเรียกใช้ API ระบบจะพิจารณา เวลาที่สามารถหยุดกระบวนการเหล่านี้ได้อย่างปลอดภัย โดยปกติแล้ว ระบบจะรอจนกว่าแอปของคุณจะใช้เวลาทำงานในเบื้องหลังเป็นระยะเวลานานแทนที่จะเป็นเบื้องหน้า
หากต้องการแจ้งให้ผู้ใช้ทราบว่าแอปของคุณไม่จำเป็นต้องเข้าถึงสิทธิ์รันไทม์ ที่เฉพาะเจาะจงอีกต่อไป ให้แสดงกล่องโต้ตอบในครั้งถัดไปที่ผู้ใช้เปิดแอป กล่องโต้ตอบนี้อาจมีรายการสิทธิ์
รีเซ็ตสิทธิ์ของแอปที่ไม่ได้ใช้โดยอัตโนมัติ
หากแอปกำหนดเป้าหมายเป็น Android 11 (API ระดับ 30) ขึ้นไปและไม่มีการใช้งานเป็นเวลา 2-3 เดือน ระบบจะปกป้องข้อมูลผู้ใช้โดยการรีเซ็ตโดยอัตโนมัติ สิทธิ์รันไทม์ที่ละเอียดอ่อนซึ่งผู้ใช้ได้ให้แก่แอปของคุณ ดูข้อมูลเพิ่มเติมได้ใน คำแนะนำเกี่ยวกับการพักแอป
ขอเป็นตัวแฮนเดิลเริ่มต้นหากจำเป็น
แอปบางแอปต้องอาศัยการเข้าถึงข้อมูลที่ละเอียดอ่อนของผู้ใช้ที่เกี่ยวข้องกับบันทึกการโทร และข้อความ SMS หากต้องการขอสิทธิ์ที่เฉพาะเจาะจงสำหรับบันทึกการโทร และข้อความ SMS รวมถึงเผยแพร่แอปของคุณไปยัง Play Store คุณต้องแจ้งให้ ผู้ใช้ตั้งค่าแอปของคุณเป็นตัวแฮนเดิลเริ่มต้นสำหรับฟังก์ชันหลักของระบบก่อน ที่จะขอสิทธิ์รันไทม์เหล่านี้
ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวแฮนเดิลเริ่มต้น รวมถึงคำแนะนำเกี่ยวกับการแสดงข้อความแจ้งตัวแฮนเดิลเริ่มต้นต่อผู้ใช้ได้ที่ดูคำแนะนำเกี่ยวกับสิทธิ์ที่ใช้ในตัวแฮนเดิลเริ่มต้นเท่านั้น
ให้สิทธิ์รันไทม์ทั้งหมดเพื่อวัตถุประสงค์ในการทดสอบ
หากต้องการให้สิทธิ์รันไทม์ทั้งหมดโดยอัตโนมัติเมื่อติดตั้งแอปใน
โปรแกรมจำลองหรืออุปกรณ์ทดสอบ ให้ใช้ตัวเลือก -g สำหรับคำสั่ง adb shell install
 ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
adb shell install -g PATH_TO_APK_FILE
แหล่งข้อมูลเพิ่มเติม
อ่านข้อมูลเพิ่มเติมเกี่ยวกับสิทธิ์ได้ในบทความต่อไปนี้
ดูข้อมูลเพิ่มเติมเกี่ยวกับการขอสิทธิ์ได้ที่ตัวอย่างสิทธิ์
นอกจากนี้ คุณยังทำตาม codelab นี้ที่แสดงแนวทางปฏิบัติแนะนำด้านความเป็นส่วนตัวได้ด้วย
[12]: /reference/androidx/core/content/ContextCompat#checkSelfPermission(android.content.Context, java.lang.String)
[15]: /reference/androidx/core/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int) [16]: /reference/androidx/core/app/ActivityCompat#shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String)
