ขอสิทธิ์รันไทม์

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

หากคุณประกาศสิทธิ์ที่เป็นอันตราย และหากแอปของคุณติดตั้งในอุปกรณ์ที่ใช้ Android 6.0 (API ระดับ 23) ขึ้นไป คุณต้องขอสิทธิ์ที่เป็นอันตรายในขณะรันไทม์โดยทำตามขั้นตอนในคำแนะนำนี้

หากคุณไม่ได้ประกาศสิทธิ์ที่เป็นอันตราย หรือหากแอปของคุณติดตั้งใน อุปกรณ์ที่ใช้ Android 5.1 (API ระดับ 22) หรือต่ำกว่า ระบบจะ ให้สิทธิ์โดยอัตโนมัติ และคุณไม่จำเป็นต้องทำขั้นตอนที่เหลือ ในหน้านี้

หลักการพื้นฐาน

หลักการพื้นฐานในการขอสิทธิ์ในระหว่างรันไทม์มีดังนี้

  • ขอสิทธิ์ในบริบทเมื่อผู้ใช้เริ่มโต้ตอบกับ ฟีเจอร์ที่ต้องใช้สิทธิ์
  • อย่าบล็อกผู้ใช้ ให้ตัวเลือกในการยกเลิกโฟลว์ UI เพื่อการศึกษาเสมอ เช่น โฟลว์ที่อธิบายเหตุผลในการขอสิทธิ์
  • หากผู้ใช้ปฏิเสธหรือเพิกถอนสิทธิ์ที่ฟีเจอร์หนึ่งต้องใช้ ให้ลดแอปลงอย่างค่อยเป็นค่อยไปเพื่อให้ผู้ใช้ใช้แอปของคุณต่อไปได้ ซึ่งอาจทำได้โดยการปิดใช้ฟีเจอร์ที่ต้องใช้สิทธิ์
  • อย่าคาดเดาพฤติกรรมของระบบ เช่น อย่าคิดว่าสิทธิ์ จะปรากฏในกลุ่มสิทธิ์เดียวกัน กลุ่มสิทธิ์เพียงช่วยให้ระบบลดจำนวนกล่องโต้ตอบของระบบที่แสดงต่อผู้ใช้เมื่อแอปขอสิทธิ์ที่เกี่ยวข้องอย่างใกล้ชิด

เวิร์กโฟลว์สำหรับการขอสิทธิ์

ก่อนที่จะประกาศและขอสิทธิ์รันไทม์ในแอป ให้ประเมิน ว่าแอปของคุณจำเป็นต้องทำเช่นนั้นหรือไม่ คุณสามารถ ตอบสนอง Use Case หลายอย่างในแอป เช่น การถ่ายรูป การหยุดเล่นสื่อชั่วคราว และการแสดงโฆษณาที่เกี่ยวข้อง โดยไม่ต้องประกาศสิทธิ์ใดๆ

หากสรุปว่าแอปของคุณต้องประกาศและขอสิทธิ์รันไทม์ ให้ทำตามขั้นตอนต่อไปนี้

  1. ในไฟล์ Manifest ของแอป ให้ประกาศสิทธิ์ที่แอปอาจต้อง ขอ
  2. ออกแบบ UX ของแอปเพื่อให้การดำเนินการที่เฉพาะเจาะจงในแอปเชื่อมโยงกับ สิทธิ์รันไทม์ที่เฉพาะเจาะจง แจ้งให้ผู้ใช้ทราบว่าการดำเนินการใดที่อาจกำหนดให้ผู้ใช้ ต้องให้สิทธิ์แอปของคุณในการเข้าถึงข้อมูลส่วนตัวของผู้ใช้
  3. รอให้ผู้ใช้เรียกใช้ฟังก์ชันหรือการดำเนินการในแอปของคุณ ซึ่งต้องเข้าถึงข้อมูลส่วนตัวของผู้ใช้ที่เฉพาะเจาะจง ในเวลานั้น แอปของคุณจะ ขอสิทธิ์รันไทม์ที่จำเป็นสำหรับการเข้าถึงข้อมูลดังกล่าวได้
  4. ตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์รันไทม์ที่แอปของคุณต้องการแล้วหรือยัง หากเป็นเช่นนั้น แอปของคุณจะเข้าถึง ข้อมูลส่วนตัวของผู้ใช้ได้ หากไม่ ให้ลองทำขั้นตอนถัดไป

    คุณต้องตรวจสอบว่ามีสิทธิ์ทุกครั้งที่ดำเนินการซึ่งต้องใช้สิทธิ์นั้น

  5. ตรวจสอบว่าแอปควรแสดงเหตุผลต่อผู้ใช้หรือไม่ เพื่ออธิบายว่าทำไมแอปของคุณจึงต้องการให้ผู้ใช้ให้สิทธิ์รันไทม์ที่เฉพาะเจาะจง หากระบบพิจารณาว่าแอปของคุณไม่ควรแสดงเหตุผล ให้ไปที่ ขั้นตอนถัดไปโดยตรงโดยไม่ต้องแสดงองค์ประกอบ UI

    อย่างไรก็ตาม หากระบบพิจารณาว่าแอปของคุณควรแสดงเหตุผล ให้แสดงเหตุผลแก่ผู้ใช้ในองค์ประกอบ UI ในเหตุผลนี้ ให้อธิบายอย่างชัดเจนว่าแอปของคุณพยายามเข้าถึงข้อมูลใดและประโยชน์ที่แอปจะมอบให้แก่ผู้ใช้ได้หากผู้ใช้ให้สิทธิ์รันไทม์ หลังจาก ผู้ใช้รับทราบเหตุผลแล้ว ให้ไปยังขั้นตอนถัดไป

  6. ขอสิทธิ์รันไทม์ที่แอปของคุณต้องใช้ เพื่อเข้าถึงข้อมูลส่วนตัวของผู้ใช้ ระบบจะแสดงข้อความแจ้งขอสิทธิ์รันไทม์ เช่น ข้อความที่แสดงในหน้าภาพรวมของสิทธิ์

  7. ตรวจสอบคำตอบของผู้ใช้ว่าเลือกที่จะให้หรือปฏิเสธสิทธิ์รันไทม์

  8. หากผู้ใช้ให้สิทธิ์แก่แอป คุณจะเข้าถึงข้อมูลผู้ใช้ส่วนตัวได้ หากผู้ใช้ปฏิเสธสิทธิ์แทน ให้ลดประสบการณ์การใช้งานแอปลงอย่างค่อยเป็นค่อยไปเพื่อให้ผู้ใช้ใช้งานฟังก์ชันได้ โดยไม่ต้องใช้ข้อมูลที่ได้รับการปกป้องโดยสิทธิ์ดังกล่าว

รูปที่ 1 แสดงเวิร์กโฟลว์และชุดการตัดสินใจที่เกี่ยวข้องกับกระบวนการนี้

รูปที่ 1 แผนภาพที่แสดงเวิร์กโฟลว์สำหรับการประกาศและ การขอสิทธิ์รันไทม์ใน Android

พิจารณาว่าแอปได้รับสิทธิ์แล้วหรือไม่

หากต้องการตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์เฉพาะแก่แอปของคุณแล้วหรือไม่ ให้ส่งสิทธิ์นั้นไปยังเมธอด 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 ของโมดูล

จากนั้นคุณจะใช้คลาสใดคลาสหนึ่งต่อไปนี้ได้

  • หากต้องการขอสิทธิ์เดียว ให้ใช้ RequestPermission
  • หากต้องการขอสิทธิ์หลายรายการพร้อมกัน ให้ใช้ RequestMultiplePermissions

ขั้นตอนต่อไปนี้แสดงวิธีใช้RequestPermissionสัญญา กระบวนการนี้เกือบจะเหมือนกับRequestMultiplePermissionsสัญญา

  1. ในตรรกะการเริ่มต้นของกิจกรรมหรือ Fragment ให้ส่งการติดตั้งใช้งาน ของ ActivityResultCallback ในการเรียกใช้ registerForActivityResult() ActivityResultCallback จะกำหนดวิธีที่แอปจัดการคำตอบของผู้ใช้ต่อ คำขอสิทธิ์

    เก็บการอ้างอิงไปยังค่าที่ฟังก์ชัน registerForActivityResult() แสดงผล ซึ่งมีประเภทเป็น ActivityResultLauncher

  2. หากต้องการแสดงกล่องโต้ตอบสิทธิ์ของระบบเมื่อจำเป็น ให้เรียกใช้เมธอด 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 ของสิทธิ์

สิทธิ์ครั้งเดียว

ตัวเลือกที่ชื่อ &quot;เฉพาะครั้งนี้&quot; เป็นปุ่มที่ 2 จาก 3 ปุ่มใน
    กล่องโต้ตอบ
รูปที่ 2 กล่องโต้ตอบของระบบที่ปรากฏขึ้นเมื่อแอปขอ สิทธิ์แบบครั้งเดียว

ตั้งแต่ Android 11 (API ระดับ 30) เป็นต้นไป เมื่อใดก็ตามที่แอปขอสิทธิ์ ที่เกี่ยวข้องกับตำแหน่ง ไมโครโฟน หรือกล้อง กล่องโต้ตอบสิทธิ์ที่แสดงต่อผู้ใช้ จะมีตัวเลือกที่ชื่อเฉพาะครั้งนี้ ดังที่แสดงในรูปที่ 2 หากผู้ใช้เลือกตัวเลือกนี้ในกล่องโต้ตอบ แอปของคุณจะได้รับสิทธิ์แบบครั้งเดียวชั่วคราว

จากนั้นแอปจะเข้าถึงข้อมูลที่เกี่ยวข้องได้เป็นระยะเวลาหนึ่งซึ่งขึ้นอยู่กับ พฤติกรรมของแอปและการกระทําของผู้ใช้ ดังนี้

  • ขณะที่กิจกรรมของแอปปรากฏอยู่ แอปจะเข้าถึงข้อมูลได้
  • หากผู้ใช้ส่งแอปของคุณไปทำงานเบื้องหลัง แอปจะยังเข้าถึงข้อมูลได้ในช่วงเวลาสั้นๆ
  • หากคุณเปิดใช้บริการที่ทำงานอยู่เบื้องหน้าขณะที่กิจกรรมแสดงอยู่ และผู้ใช้ ย้ายแอปไปทำงานที่เบื้องหลัง แอปจะยังคงเข้าถึงข้อมูลได้ จนกว่าบริการที่ทำงานอยู่เบื้องหน้าจะหยุดทำงาน

กระบวนการของแอปจะสิ้นสุดเมื่อมีการเพิกถอนสิทธิ์

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

เมื่อผู้ใช้เปิดแอปของคุณในครั้งถัดไปและฟีเจอร์ในแอปขอสิทธิ์เข้าถึง ตำแหน่ง ไมโครโฟน หรือกล้อง ระบบจะแจ้งให้ผู้ใช้ขอสิทธิ์อีกครั้ง

รีเซ็ตสิทธิ์ที่ไม่ได้ใช้

Android มีหลายวิธีในการรีเซ็ตสิทธิ์รันไทม์ที่ไม่ได้ใช้ให้กลับไปอยู่ใน สถานะเริ่มต้นที่ถูกปฏิเสธ ดังนี้

นำสิทธิ์เข้าถึงของแอปออก

ใน 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 นี้ที่แสดงแนวทางปฏิบัติแนะนำด้านความเป็นส่วนตัวได้ด้วย