แอป Android ทุกแอปจะทำงานในแซนด์บ็อกซ์ที่มีสิทธิ์เข้าถึงแบบจำกัด หากแอปของคุณจำเป็นต้องใช้ ทรัพยากรหรือข้อมูลที่อยู่นอกแซนด์บ็อกซ์ของตัวเอง คุณสามารถประกาศสิทธิ์รันไทม์ และตั้งค่าคำขอสิทธิ์ ที่ให้สิทธิ์เข้าถึงนี้ได้ ขั้นตอนเหล่านี้เป็นส่วนหนึ่งของเวิร์กโฟลว์สำหรับการใช้ สิทธิ์
หากคุณประกาศสิทธิ์ที่เป็นอันตราย และหากแอปของคุณติดตั้งในอุปกรณ์ที่ใช้ Android 6.0 (API ระดับ 23) ขึ้นไป คุณต้องขอสิทธิ์ที่เป็นอันตรายในขณะรันไทม์โดยทำตามขั้นตอนในคำแนะนำนี้
หากคุณไม่ได้ประกาศสิทธิ์ที่เป็นอันตราย หรือหากแอปของคุณติดตั้งใน อุปกรณ์ที่ใช้ Android 5.1 (API ระดับ 22) หรือต่ำกว่า ระบบจะ ให้สิทธิ์โดยอัตโนมัติ และคุณไม่จำเป็นต้องทำขั้นตอนที่เหลือ ในหน้านี้
หลักการพื้นฐาน
หลักการพื้นฐานในการขอสิทธิ์ในระหว่างรันไทม์มีดังนี้
- ขอสิทธิ์ในบริบทเมื่อผู้ใช้เริ่มโต้ตอบกับ ฟีเจอร์ที่ต้องใช้สิทธิ์
- อย่าบล็อกผู้ใช้ ให้ตัวเลือกในการยกเลิกโฟลว์ UI เพื่อการศึกษาเสมอ เช่น โฟลว์ที่อธิบายเหตุผลในการขอสิทธิ์
- หากผู้ใช้ปฏิเสธหรือเพิกถอนสิทธิ์ที่ฟีเจอร์หนึ่งต้องใช้ ให้ลดแอปลงอย่างค่อยเป็นค่อยไปเพื่อให้ผู้ใช้ใช้แอปของคุณต่อไปได้ ซึ่งอาจทำได้โดยการปิดใช้ฟีเจอร์ที่ต้องใช้สิทธิ์
- อย่าคาดเดาพฤติกรรมของระบบ เช่น อย่าคิดว่าสิทธิ์ จะปรากฏในกลุ่มสิทธิ์เดียวกัน กลุ่มสิทธิ์เพียงช่วยให้ระบบลดจำนวนกล่องโต้ตอบของระบบที่แสดงต่อผู้ใช้เมื่อแอปขอสิทธิ์ที่เกี่ยวข้องอย่างใกล้ชิด
เวิร์กโฟลว์สำหรับการขอสิทธิ์
ก่อนที่จะประกาศและขอสิทธิ์รันไทม์ในแอป ให้ประเมิน ว่าแอปของคุณจำเป็นต้องทำเช่นนั้นหรือไม่ คุณสามารถ ตอบสนอง Use Case หลายอย่างในแอป เช่น การถ่ายรูป การหยุดเล่นสื่อชั่วคราว และการแสดงโฆษณาที่เกี่ยวข้อง โดยไม่ต้องประกาศสิทธิ์ใดๆ
หากสรุปว่าแอปของคุณต้องประกาศและขอสิทธิ์รันไทม์ ให้ทำตามขั้นตอนต่อไปนี้
- ในไฟล์ Manifest ของแอป ให้ประกาศสิทธิ์ที่แอปอาจต้อง ขอ
- ออกแบบ UX ของแอปเพื่อให้การดำเนินการที่เฉพาะเจาะจงในแอปเชื่อมโยงกับ สิทธิ์รันไทม์ที่เฉพาะเจาะจง แจ้งให้ผู้ใช้ทราบว่าการดำเนินการใดที่อาจกำหนดให้ผู้ใช้ ต้องให้สิทธิ์แอปของคุณในการเข้าถึงข้อมูลส่วนตัวของผู้ใช้
- รอให้ผู้ใช้เรียกใช้ฟังก์ชันหรือการดำเนินการในแอปของคุณ ซึ่งต้องเข้าถึงข้อมูลส่วนตัวของผู้ใช้ที่เฉพาะเจาะจง ในเวลานั้น แอปของคุณจะ ขอสิทธิ์รันไทม์ที่จำเป็นสำหรับการเข้าถึงข้อมูลดังกล่าวได้
ตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์รันไทม์ที่แอปของคุณต้องการแล้วหรือยัง หากเป็นเช่นนั้น แอปของคุณจะเข้าถึง ข้อมูลส่วนตัวของผู้ใช้ได้ หากไม่ ให้ลองทำขั้นตอนถัดไป
คุณต้องตรวจสอบว่ามีสิทธิ์ทุกครั้งที่ดำเนินการซึ่งต้องใช้สิทธิ์นั้น
ตรวจสอบว่าแอปควรแสดงเหตุผลต่อผู้ใช้หรือไม่ เพื่ออธิบายว่าทำไมแอปของคุณจึงต้องการให้ผู้ใช้ให้สิทธิ์รันไทม์ที่เฉพาะเจาะจง หากระบบพิจารณาว่าแอปของคุณไม่ควรแสดงเหตุผล ให้ไปที่ ขั้นตอนถัดไปโดยตรงโดยไม่ต้องแสดงองค์ประกอบ UI
อย่างไรก็ตาม หากระบบพิจารณาว่าแอปของคุณควรแสดงเหตุผล ให้แสดงเหตุผลแก่ผู้ใช้ในองค์ประกอบ UI ในเหตุผลนี้ ให้อธิบายอย่างชัดเจนว่าแอปของคุณพยายามเข้าถึงข้อมูลใดและประโยชน์ที่แอปจะมอบให้แก่ผู้ใช้ได้หากผู้ใช้ให้สิทธิ์รันไทม์ หลังจาก ผู้ใช้รับทราบเหตุผลแล้ว ให้ไปยังขั้นตอนถัดไป
ขอสิทธิ์รันไทม์ที่แอปของคุณต้องใช้ เพื่อเข้าถึงข้อมูลส่วนตัวของผู้ใช้ ระบบจะแสดงข้อความแจ้งขอสิทธิ์รันไทม์ เช่น ข้อความที่แสดงในหน้าภาพรวมของสิทธิ์
ตรวจสอบคำตอบของผู้ใช้ว่าเลือกที่จะให้หรือปฏิเสธสิทธิ์รันไทม์
หากผู้ใช้ให้สิทธิ์แก่แอป คุณจะเข้าถึงข้อมูลผู้ใช้ส่วนตัวได้ หากผู้ใช้ปฏิเสธสิทธิ์แทน ให้ลดประสบการณ์การใช้งานแอปลงอย่างค่อยเป็นค่อยไปเพื่อให้ผู้ใช้ใช้งานฟังก์ชันได้ โดยไม่ต้องใช้ข้อมูลที่ได้รับการปกป้องโดยสิทธิ์ดังกล่าว
รูปที่ 1 แสดงเวิร์กโฟลว์และชุดการตัดสินใจที่เกี่ยวข้องกับกระบวนการนี้
พิจารณาว่าแอปได้รับสิทธิ์แล้วหรือไม่
หากต้องการตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์เฉพาะแก่แอปของคุณแล้วหรือไม่ ให้ส่งสิทธิ์นั้นไปยังเมธอด
ContextCompat.checkSelfPermission()
เมธอดนี้จะแสดงผลเป็น PERMISSION_GRANTED
หรือ PERMISSION_DENIED
โดยขึ้นอยู่กับว่าแอปของคุณมีสิทธิ์หรือไม่
อธิบายเหตุผลที่แอปจำเป็นต้องมีสิทธิ์
กล่องโต้ตอบสิทธิ์ที่ระบบแสดงเมื่อคุณเรียกใช้
requestPermissions()
จะบอกว่าแอปต้องการสิทธิ์ใด แต่ไม่ได้บอก
เหตุผล ในบางกรณี ผู้ใช้อาจรู้สึกงุนงง ขอแนะนำให้
อธิบายให้ผู้ใช้ทราบว่าทำไมแอปของคุณจึงต้องการสิทธิ์ก่อนที่จะเรียกใช้
requestPermissions()
งานวิจัยแสดงให้เห็นว่าผู้ใช้จะรู้สึกสบายใจมากขึ้นเมื่อได้รับคำขอสิทธิ์ หากทราบว่าทำไมแอปจึงต้องขอสิทธิ์ดังกล่าว เช่น สิทธิ์นั้นจำเป็นต่อการรองรับฟีเจอร์หลักของแอปหรือเพื่อการโฆษณาหรือไม่ ดังนั้น หากคุณใช้การเรียก API เพียงบางส่วนที่อยู่ภายใต้กลุ่มสิทธิ์ การระบุอย่างชัดเจนว่าคุณใช้สิทธิ์ใดและเพราะเหตุใดจึงเป็นประโยชน์ ตัวอย่างเช่น หากคุณใช้เพียงตำแหน่งคร่าวๆ ให้ผู้ใช้ทราบในคำอธิบายแอปหรือในบทความช่วยเหลือเกี่ยวกับแอป
ในบางกรณี การแจ้งให้ผู้ใช้ทราบเกี่ยวกับการเข้าถึงข้อมูลที่ละเอียดอ่อนแบบเรียลไทม์ก็เป็นประโยชน์เช่นกัน เช่น หากคุณเข้าถึงกล้องหรือไมโครโฟน คุณควรแจ้งให้ผู้ใช้ทราบโดยใช้ไอคอนการแจ้งเตือนที่ใดที่หนึ่งในแอป หรือในถาดการแจ้งเตือน (หากแอปพลิเคชันทำงานในเบื้องหลัง) เพื่อไม่ให้ดูเหมือนว่าคุณกำลังเก็บรวบรวมข้อมูลอย่างลับๆ
ท้ายที่สุด หากคุณจำเป็นต้องขอสิทธิ์เพื่อให้บางอย่างในแอปทำงานได้ แต่ผู้ใช้ไม่เข้าใจเหตุผล ให้หาวิธีแจ้งให้ผู้ใช้ทราบว่าทำไมคุณจึงต้องขอสิทธิ์ที่ละเอียดอ่อนที่สุด
หากContextCompat.checkSelfPermission()
เมธอดแสดงผลเป็น PERMISSION_DENIED
,
ให้เรียกใช้ shouldShowRequestPermissionRationale()
หากเมธอดนี้แสดงผลเป็น true
ให้แสดง UI ให้ความรู้แก่ผู้ใช้ ใน UI นี้ ให้
อธิบายว่าทำไมฟีเจอร์ที่ผู้ใช้ต้องการเปิดใช้จึงต้องมีสิทธิ์
เฉพาะ
นอกจากนี้ หากแอปขอสิทธิ์ที่เกี่ยวข้องกับตำแหน่ง ไมโครโฟน หรือกล้อง โปรดอธิบายเหตุผลที่แอปต้องมีสิทธิ์เข้าถึง ข้อมูลนี้
ขอสิทธิ์
หลังจากที่ผู้ใช้ดู UI เพื่อการศึกษา หรือค่าที่ส่งคืนของ
shouldShowRequestPermissionRationale()
ระบุว่าคุณไม่จำเป็นต้องแสดง
UI เพื่อการศึกษา ให้ขอสิทธิ์ ผู้ใช้จะเห็นกล่องโต้ตอบสิทธิ์ของระบบ ซึ่งผู้ใช้สามารถเลือกได้ว่าจะให้สิทธิ์ใดสิทธิ์หนึ่งแก่แอปของคุณหรือไม่
โดยให้ใช้RequestPermission
สัญญาที่รวมอยู่ในไลบรารี AndroidX ซึ่งคุณอนุญาตให้ระบบจัดการ
รหัสคำขอสิทธิ์ให้คุณ เนื่องจาก
การใช้สัญญา RequestPermission
จะช่วยลดความซับซ้อนของตรรกะ จึงเป็นโซลูชันที่แนะนำ
เมื่อเป็นไปได้ อย่างไรก็ตาม หากต้องการ คุณก็จัดการรหัสคำขอ
ด้วยตนเองได้เช่นกัน ซึ่งเป็นส่วนหนึ่งของคำขอสิทธิ์ และ
รวมรหัสคำขอนี้ไว้ในตรรกะการเรียกกลับของสิทธิ์
อนุญาตให้ระบบจัดการรหัสคำขอสิทธิ์
หากต้องการอนุญาตให้ระบบจัดการรหัสคำขอที่เชื่อมโยงกับ
คำขอสิทธิ์ ให้เพิ่มการอ้างอิงในไลบรารีต่อไปนี้ในไฟล์ 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
// Register the permissions callback, which handles the user's response to the // system permissions dialog. Save the return value, an instance of // ActivityResultLauncher. You can use either a val, as shown in this snippet, // or a lateinit var in your onAttach() or onCreate() method. val requestPermissionLauncher = registerForActivityResult(RequestPermission() ) { isGranted: Boolean -> if (isGranted) { // 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. } }
Java
// Register the permissions callback, which handles the user's response to the // system permissions dialog. Save the return value, an instance of // ActivityResultLauncher, as an instance variable. private ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(new RequestPermission(), isGranted -> { if (isGranted) { // 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. } });
และข้อมูลโค้ดนี้แสดงให้เห็นกระบวนการที่แนะนำในการตรวจสอบ สิทธิ์และขอสิทธิ์จากผู้ใช้เมื่อจำเป็น
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()
ข้อมูลโค้ดต่อไปนี้แสดงวิธีขอสิทธิ์โดยใช้รหัสคำขอ
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. } }
ขอสิทธิ์เข้าถึงตำแหน่ง
เมื่อขอสิทธิ์เข้าถึงตำแหน่ง ให้ทำตามแนวทางปฏิบัติแนะนำเดียวกันกับสิทธิ์รันไทม์อื่นๆ ความแตกต่างที่สำคัญอย่างหนึ่งเมื่อพูดถึงสิทธิ์เข้าถึงตำแหน่งคือ ระบบมีสิทธิ์หลายอย่างที่เกี่ยวข้องกับตำแหน่ง สิทธิ์ที่คุณขอและวิธีขอจะขึ้นอยู่กับข้อกำหนดด้านตำแหน่งสำหรับกรณีการใช้งานของแอป
ตำแหน่งที่ทำงานอยู่เบื้องหน้า
หากแอปมีฟีเจอร์ที่แชร์หรือรับข้อมูลตำแหน่งเพียงครั้งเดียวหรือเป็นระยะเวลาที่กำหนด ฟีเจอร์นั้นจะต้องมีการเข้าถึงตำแหน่งในเบื้องหน้า ตัวอย่างเช่น
- ฟีเจอร์ในแอปนำทางช่วยให้ผู้ใช้ได้รับเส้นทางแบบเลี้ยวต่อเลี้ยว
- ฟีเจอร์ในแอปรับส่งข้อความช่วยให้ผู้ใช้แชร์ตำแหน่งปัจจุบันกับผู้ใช้รายอื่นได้
ระบบจะถือว่าแอปของคุณใช้ตำแหน่งในเบื้องหน้าหากฟีเจอร์ของ แอปเข้าถึงตำแหน่งปัจจุบันของอุปกรณ์ในสถานการณ์ใดสถานการณ์หนึ่งต่อไปนี้
- กิจกรรมที่เป็นของแอปจะปรากฏ
แอปของคุณกำลังเรียกใช้บริการที่ทำงานอยู่เบื้องหน้า เมื่อบริการที่ทำงานอยู่เบื้องหน้า ทำงานอยู่ ระบบจะเพิ่มการรับรู้ของผู้ใช้โดยแสดงการแจ้งเตือนแบบต่อเนื่อง แอปจะยังคงมีสิทธิ์เข้าถึงเมื่ออยู่ในเบื้องหลัง เช่น เมื่อผู้ใช้กดปุ่มหน้าแรกในอุปกรณ์หรือปิดจอแสดงผลของอุปกรณ์
ใน 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 คือชื่อสิทธิ์ที่ต้องการ รีเซ็ต
หากต้องการดูรายการสิทธิ์ของแอป 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 นี้ที่แสดงแนวทางปฏิบัติแนะนำด้านความเป็นส่วนตัวได้ด้วย