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

แอป 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 จะทำให้ตรรกะของคุณง่ายขึ้น เราจึงขอแนะนำวิธีแก้ปัญหานี้หากเป็นไปได้ อย่างไรก็ตาม หากจำเป็น คุณยังจัดการโค้ดคำขอด้วยตนเองเป็นส่วนหนึ่งของคำขอสิทธิ์และรวมโค้ดคำขอนี้ไว้ในตรรกะ Callback ของสิทธิ์ได้ด้วย

อนุญาตให้ระบบจัดการรหัสคำขอสิทธิ์

หากต้องการให้ระบบจัดการโค้ดคําขอที่เชื่อมโยงกับคําขอสิทธิ์ ให้เพิ่มการพึ่งพาในไลบรารีต่อไปนี้ในไฟล์ build.gradle ของโมดูล

จากนั้นคุณสามารถใช้ชั้นเรียนต่อไปนี้

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

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

  1. ในตรรกะการเริ่มต้นของกิจกรรมหรือแฟรกเมนต์ ให้ส่งผ่านการใช้งาน 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 ตัวอย่างบางส่วนมีดังนี้

  • ภายในแอปการแชร์ตำแหน่งของครอบครัว ฟีเจอร์ช่วยให้ผู้ใช้แชร์ตำแหน่งกับสมาชิกในครอบครัวได้อย่างต่อเนื่อง
  • ภายในแอป 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]
...

สิทธิ์ที่ผู้ใช้ปฏิเสธ 1 ครั้งจะมีการแจ้งว่าไม่เหมาะสมโดย USER_SET สิทธิ์ที่ถูกปฏิเสธอย่างถาวรโดยการเลือกปฏิเสธ 2 ครั้งจะได้รับการแจ้งว่าไม่อนุญาตโดย USER_FIXED

เพื่อให้แน่ใจว่าผู้ทดสอบจะเห็นกล่องโต้ตอบคำขอระหว่างการทดสอบ ให้รีเซ็ตแฟล็กเหล่านี้เมื่อแก้ไขข้อบกพร่องของแอปเสร็จแล้ว ให้ใช้คำสั่งต่อไปนี้

adb shell pm clear-permission-flags PACKAGE_NAME PERMISSION_NAME user-set user-fixed

PERMISSION_NAME คือชื่อของสิทธิ์ที่คุณต้องการรีเซ็ต

โปรดไปที่หน้าอ้างอิง API สิทธิ์เพื่อดูรายการสิทธิ์ของแอป Android ทั้งหมด

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

ตัวเลือก &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 ที่แสดงแนวทางปฏิบัติแนะนำด้านความเป็นส่วนตัวนี้ให้เสร็จสมบูรณ์ได้ด้วย