Yêu cầu quyền khi bắt đầu chạy

Mọi ứng dụng Android đều chạy trong một hộp cát bị giới hạn về quyền truy cập. Nếu ứng dụng của bạn cần sử dụng tài nguyên hoặc thông tin bên ngoài hộp cát của ứng dụng đó, thì bạn có thể khai báo một quyền khi bắt đầu chạy và thiết lập yêu cầu quyền để cấp quyền truy cập này. Các bước này là một phần trong quy trình sử dụng quyền.

Nếu bạn khai báo bất kỳ quyền nguy hiểm nào và ứng dụng của bạn được cài đặt trên thiết bị chạy Android 6.0 (API cấp 23) trở lên, bạn phải yêu cầu các quyền nguy hiểm đó trong thời gian chạy bằng cách làm theo các bước trong hướng dẫn này.

Nếu bạn không khai báo bất kỳ quyền nguy hiểm nào hoặc nếu ứng dụng được cài đặt trên thiết bị chạy Android 5.1 (API cấp 22) trở xuống, thì các quyền đó sẽ tự động được cấp và bạn không cần hoàn thành bất kỳ bước nào còn lại trên trang này.

Nguyên tắc cơ bản

Nguyên tắc cơ bản để yêu cầu cấp quyền trong thời gian chạy bao gồm:

  • Yêu cầu cấp quyền trong bối cảnh khi người dùng bắt đầu tương tác với tính năng yêu cầu quyền đó.
  • Không chặn người dùng. Luôn cung cấp tuỳ chọn huỷ quy trình giao diện hướng dẫn người dùng, chẳng hạn như quy trình giải thích lý do yêu cầu cấp quyền.
  • Nếu người dùng từ chối hoặc thu hồi quyền mà một tính năng cần đến, nên xuống cấp nhẹ với ứng dụng để người dùng tiếp tục dùng ứng dụng, có thể là bằng cách tắt tính năng yêu cầu cấp quyền đó.
  • Không giả định bất kỳ hành vi hệ thống nào. Ví dụ: không giả định rằng quyền xuất hiện trong cùng một nhóm quyền. Nhóm quyền chỉ giúp hệ thống giảm thiểu tối đa số lượng hộp thoại hệ thống hiển thị với người dùng khi ứng dụng yêu cầu cấp quyền có liên quan chặt chẽ.

Quy trình yêu cầu cấp quyền

Trước khi bạn khai báo và yêu cầu cấp quyền khi bắt đầu chạy trong ứng dụng, hãy đánh giá xem liệu ứng dụng có cần làm như vậy hay không. Bạn có thể thực hiện nhiều trường hợp sử dụng trong ứng dụng của mình, chẳng hạn như chụp ảnh, tạm dừng việc phát nội dung nghe nhìn và hiển thị quảng cáo phù hợp mà không cần khai báo bất kỳ quyền nào.

Nếu bạn kết luận rằng ứng dụng cần khai báo và yêu cầu cấp quyền khi bắt đầu chạy, thì hoàn tất các bước sau:

  1. Trong tệp kê khai của ứng dụng, khai báo các quyền mà ứng dụng của bạn có thể yêu cầu.
  2. Thiết kế trải nghiệm người dùng trên ứng dụng của bạn để hành động cụ thể trong ứng dụng liên kết với các quyền cụ thể khi bắt đầu chạy. Cho người dùng biết những hành động nào có thể yêu cầu họ cấp quyền cho ứng dụng truy cập vào dữ liệu riêng tư của người dùng.
  3. Chờ người dùng gọi ra nhiệm vụ hoặc thao tác trong ứng dụng mà yêu cầu quyền truy cập vào dữ liệu riêng tư của người dùng. Tại thời điểm đó, ứng dụng của bạn có thể yêu cầu cấp quyền khi bắt đầu chạy cần đến để truy cập dữ liệu đó.
  4. Kiểm tra xem liệu người dùng đã cấp quyền khi bắt đầu chạy mà ứng dụng của bạn yêu cầu chưa. Nếu có, ứng dụng của bạn có thể truy cập dữ liệu riêng tư của người dùng. Nếu không, chuyển sang bước tiếp theo.

    Bạn phải kiểm tra xem liệu mình có quyền hay không mỗi lần thực hiện một thao tác yêu cầu quyền đó.

  5. Kiểm tra liệu ứng dụng của bạn có cho biết lý do hay không, giải thích với người dùng tại sao ứng dụng của bạn cần người dùng cấp quyền cụ thể khi bắt đầu chạy. Nếu hệ thống xác định rằng ứng dụng không nên cho biết lý do, thì tiếp tục thực hiện ngay bước tiếp theo mà không cần hiển thị thành phần giao diện người dùng.

    Tuy nhiên, nếu hệ thống xác định rằng ứng dụng của bạn cần cho biết lý do, thì nên trình bày lý do với người dùng trong một thành phần trên giao diện người dùng. Trong lý do này, hãy giải thích rõ ràng ứng dụng của bạn đang cố truy cập vào dữ liệu nào và ứng dụng cung cấp quyền lợi gì cho người dùng nếu họ cấp quyền khi bắt đầu chạy. Sau khi người dùng xác nhận lý do, tiếp tục chuyển sang bước tiếp theo.

  6. Yêu cầu cấp quyền khi bắt đầu chạy mà ứng dụng của bạn cần để truy cập vào dữ liệu riêng tư của người dùng. Hệ thống sẽ hiển thị lời nhắc cấp quyền khi bắt đầu chạy, chẳng hạn như lời nhắc xuất hiện trên trang tổng quan về cấp quyền.

  7. Kiểm tra phản hồi của người dùng, xem họ đã chọn cấp hay từ chối cấp quyền khi bắt đầu chạy.

  8. Nếu người dùng đã cấp quyền cho ứng dụng, bạn có thể truy cập vào dữ liệu riêng tư của người dùng. Thay vào đó, nếu người dùng từ chối cấp quyền, hãy xuống cấp nhẹ cho trải nghiệm ứng dụng của bạn để cung cấp chức năng cho người dùng khi không có thông tin được bảo vệ bằng quyền đó.

Hình 1 minh hoạ quy trình làm việc và tập hợp các quyết định liên quan đến quy trình này:

Hình 1. Sơ đồ cho thấy quy trình khai báo và yêu cầu cấp quyền khi bắt đầu chạy trên Android.

Xác định xem liệu ứng dụng của bạn đã được cấp quyền hay chưa

Để kiểm tra xem người dùng đã cấp cho ứng dụng một quyền cụ thể hay chưa, hãy chuyển quyền đó vào phương thức ContextCompat.checkSelfPermission(). Phương thức này trả về PERMISSION_GRANTED hoặc PERMISSION_DENIED, tuỳ thuộc vào việc ứng dụng của bạn có quyền hay không.

Giải thích vì sao ứng dụng của bạn cần quyền truy cập

Hộp thoại cấp quyền được hệ thống hiển thị khi bạn gọi requestPermissions() cho biết ứng dụng muốn có quyền gì, nhưng không cho biết lý do vì sao. Trong một số trường hợp, người dùng có thể thấy khó hiểu. Nên giải thích cho người dùng tại sao ứng dụng muốn có quyền trước khi bạn gọi requestPermissions().

Nghiên cứu cho thấy người dùng cảm thấy thoải mái hơn nhiều với các yêu cầu cấp quyền nếu họ biết lý do vì sao ứng dụng cần những quyền đó, chẳng hạn như việc quyền có cần thiết để hỗ trợ một tính năng cốt lõi của ứng dụng hay cho mục đích quảng cáo không. Do đó, nếu bạn chỉ sử dụng một phần các lệnh gọi API thuộc một nhóm quyền, thì việc này sẽ giúp bạn liệt kê rõ ràng những quyền bạn đang sử dụng và lý do bạn làm như vậy. Ví dụ: nếu bạn chỉ sử dụng vị trí gần đúng, hãy cho người dùng biết điều này trong phần mô tả ứng dụng hoặc trong các bài viết trợ giúp về ứng dụng của bạn.

Trong một số tình huống nhất định, việc cho người dùng biết về quyền truy cập dữ liệu nhạy cảm theo thời gian thực cũng sẽ rất hữu ích. Ví dụ: nếu đang truy cập vào máy ảnh hoặc micrô, bạn nên cho người dùng biết bằng biểu tượng thông báo ở một nơi nào đó trong ứng dụng hoặc trong khay thông báo (nếu ứng dụng đang chạy ở chế độ nền), để không mang lại cảm giác là bạn đang lén lút thu thập dữ liệu.

Cuối cùng, nếu cần một quyền để khiến tính năng nào đó trong ứng dụng hoạt động, nhưng người dùng lại không rõ lý do, hãy tìm cách cho người dùng biết tại sao bạn cần các quyền truy cập thông tin nhạy cảm nhất.

Nếu phương thức ContextCompat.checkSelfPermission() trả về PERMISSION_DENIED, hãy gọi shouldShowRequestPermissionRationale(). Nếu phương thức này trả về true, hãy hiển thị giao diện hướng dẫn người dùng cho người dùng. Trong giao diện người dùng này, hãy mô tả lý do tính năng, mà người dùng muốn bật, cần có một quyền cụ thể.

Ngoài ra, nếu ứng dụng của bạn yêu cầu một quyền liên quan đến vị trí, micrô hoặc máy ảnh, hãy cân nhắc giải thích lý do ứng dụng của bạn cần quyền truy cập vào thông tin này.

Yêu cầu cấp quyền

Hãy yêu cầu cấp quyền sau khi người dùng xem giao diện hướng dẫn người dùng hoặc giá trị trả về của shouldShowRequestPermissionRationale() cho biết bạn không cần hiển thị giao diện hướng dẫn người dùng. Người dùng sẽ thấy hộp thoại cấp quyền của hệ thống, trong đó họ có thể chọn có cấp một quyền cụ thể cho ứng dụng của bạn hay không.

Để làm điều này, hãy sử dụng contract RequestPermission có ở thư viện AndroidX. Qua đó bạn sẽ cho phép hệ thống quản lý mã yêu cầu quyền cho bạn. Vì việc sử dụng hợp đồng RequestPermission giúp đơn giản hoá logic, do vậy, bạn nên dùng giải pháp này khi có thể. Tuy nhiên, nếu cần thì bạn cũng có thể tự quản lý mã yêu cầu trong yêu cầu quyền và đưa mã yêu cầu này vào logic gọi lại quyền của mình.

Cho phép hệ thống quản lý mã yêu cầu quyền

Để cho phép hệ thống quản lý mã yêu cầu được liên kết với yêu cầu cấp quyền, thêm các phần phụ thuộc có trong những thư viện sau vào tệp build.gradle của mô-đun:

Sau đó, bạn có thể sử dụng một trong các lớp sau:

Các bước sau đây cho biết cách sử dụng hợp đồng RequestPermission. Quy trình gần giống với quy trình cho hợp đồng RequestMultiplePermissions.

  1. Trong hoạt động hoặc logic khởi chạy mảnh của bạn, chuyển việc triển khai ActivityResultCallback vào lệnh gọi đến registerForActivityResult(). ActivityResultCallback xác định cách ứng dụng của bạn xử lý phản hồi của người dùng với yêu cầu quyền.

    Duy trì tham chiếu đến giá trị trả về của registerForActivityResult(), thuộc loại ActivityResultLauncher.

  2. Để hiển thị hộp thoại cấp quyền của hệ thống khi cần, gọi phương thức launch() trên thực thể của ActivityResultLauncher mà bạn đã lưu ở bước trước.

    Sau khi launch() được gọi, hộp thoại cấp quyền của hệ thống sẽ xuất hiện. Khi người dùng chọn, hệ thống sẽ không đồng bộ gọi cách triển khai ActivityResultCallback đã được bạn xác định ở bước trước.

    Lưu ý: Ứng dụng của bạn không thể tuỳ chỉnh hộp thoại xuất hiện khi bạn gọi launch(). Để cung cấp thêm thông tin hoặc ngữ cảnh đến người dùng, hãy thay đổi giao diện người dùng của ứng dụng để người dùng dễ dàng hiểu lý do vì sao một tính năng trong ứng dụng của bạn cần một quyền cụ thể. Ví dụ: bạn có thể thay đổi văn bản trong nút để bật tính năng này.

    Ngoài ra, văn bản trong hộp thoại cấp quyền của hệ thống tham chiếu đến nhóm quyền được liên kết với quyền mà bạn đã yêu cầu. Nhóm quyền này được thiết kế để giúp hệ thống dễ dùng và ứng dụng của bạn không nên dựa vào các quyền nằm bên trong hoặc bên ngoài một nhóm quyền cụ thể.

Đoạn mã sau đây cho thấy cách xử lý phản hồi cấp quyền:

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.
        }
    });

Đoạn mã này minh hoạ quy trình đề xuất để kiểm tra một quyền và yêu cầu người dùng cấp quyền khi cần:

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);
}

Tự quản lý mã yêu cầu quyền

Thay vì cho phép hệ thống quản lý mã yêu cầu quyền, bạn có thể tự quản lý mã yêu cầu quyền này. Để thực hiện việc này, đưa mã yêu cầu vào lệnh gọi requestPermissions().

Đoạn mã sau đây minh hoạ cách yêu cầu cấp quyền bằng mã yêu cầu:

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);
}

Sau khi người dùng phản hồi với hộp thoại cấp quyền hệ thống, thì hệ thống sẽ gọi chế độ triển khai onRequestPermissionsResult() của ứng dụng. Hệ thống sẽ chuyển phản hồi người dùng tới hộp thoại cấp quyền, cũng như mã yêu cầu mà bạn đã xác định, như được cho thấy trong đoạn mã sau:

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.
    }
}

Yêu cầu quyền truy cập thông tin vị trí

Khi bạn yêu cầu cấp quyền truy cập thông tin vị trí, hãy làm theo các phương pháp hay nhất như đối với mọi quyền khi bắt đầu chạy khác. Một điểm khác biệt quan trọng khi nói đến cấp quyền truy cập thông tin vị trí là hệ thống bao gồm nhiều quyền liên quan đến vị trí. Quyền bạn yêu cầu và cách bạn yêu cầu quyền đó tuỳ thuộc vào các yêu cầu về vị trí đối với trường hợp sử dụng của ứng dụng.

Vị trí nền trước

Nếu ứng dụng của bạn chứa một tính năng chỉ chia sẻ hoặc nhận thông tin vị trí một lần hoặc trong một khoảng thời gian xác định, thì tính năng đó sẽ yêu cầu quyền truy cập thông tin vị trí ở chế độ nền trước. Sau đây là một số ví dụ:

  • Trong ứng dụng đi theo chỉ dẫn, một tính năng cho phép người dùng xem đường đi từng chặng.
  • Trong ứng dụng nhắn tin, một tính năng cho phép người dùng chia sẻ vị trí hiện tại của mình với người dùng khác.

Hệ thống sẽ coi ứng dụng của bạn đang dùng thông tin vị trí ở chế độ nền trước nếu một tính năng của ứng dụng truy cập vào vị trí hiện tại của thiết bị ở một trong các tình huống sau:

  • Một hoạt động thuộc ứng dụng của bạn đang hiển thị.
  • Ứng dụng của bạn đang chạy một dịch vụ trên nền trước. Khi một dịch vụ trên nền trước đang chạy, hệ thống sẽ báo cho người dùng biết bằng cách hiển thị một thông báo liên tục. Ứng dụng của bạn vẫn giữ quyền truy cập khi được đặt vào chế độ nền, chẳng hạn như khi người dùng nhấn nút Màn hình chính trên thiết bị hoặc tắt màn hình thiết bị.

    Trên Android 10 (API cấp 29) trở lên, bạn phải khai báo loại dịch vụ trên nền trướclocation, như được minh hoạ trong đoạn mã sau. Trên các phiên bản Android cũ, bạn nên khai báo loại dịch vụ trên nền trước này.

    <!-- 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>

Bạn khai báo nhu cầu về thông tin vị trí ở chế độ nền trước khi ứng dụng yêu cầu cấp quyền ACCESS_COARSE_LOCATION hoặc quyền ACCESS_FINE_LOCATION, như được hiển thị trong đoạn mã sau:

<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>

Quyền truy cập thông tin vị trí ở chế độ nền

Ứng dụng sẽ yêu cầu quyền truy cập thông tin vị trí ở chế độ nền nếu một tính năng trong ứng dụng đó liên tục chia sẻ vị trí với người dùng khác hoặc sử dụng (Geofencing API) API Khoanh vùng địa lý. Sau đây là một số ví dụ:

  • Trong ứng dụng chia sẻ vị trí với gia đình, một tính năng cho phép người dùng liên tục chia sẻ vị trí với thành viên gia đình.
  • Trong ứng dụng IoT, một tính năng cho phép người dùng định cấu hình các thiết bị trong nhà để tắt thiết bị khi người dùng rời khỏi nhà và bật lại khi người dùng trở về nhà.

Hệ thống sẽ coi ứng dụng của bạn đang sử dụng quyền truy cập thông tin vị trí ở chế độ nền nếu ứng dụng truy cập vào thông tin vị trí hiện tại của thiết bị trong bất kỳ tình huống nào, trừ những tình huống được mô tả trong phần thông tin vị trí ở chế độ nền trước. Độ chính xác của vị trí ở chế độ nền giống như độ chính xác của vị trí ở chế độ nền trước, tuỳ thuộc vào quyền truy cập thông tin vị trí mà ứng dụng của bạn khai báo.

Trên Android 10 (API cấp 29) trở lên, bạn phải khai báo ACCESS_BACKGROUND_LOCATIONquyền trong tệp kê khai của ứng dụng để yêu cầu quyền truy cập thông tin vị trí ở chế độ nền trong thời gian chạy. Trên các phiên bản Android cũ, khi ứng dụng nhận được quyền truy cập thông tin vị trí ở chế độ nền trước, ứng dụng đó cũng tự động nhận được quyền truy cập thông tin vị trí ở chế độ nền.

<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>

Xử lý việc từ chối cấp quyền

Nếu người dùng từ chối một yêu cầu quyền, ứng dụng của bạn sẽ giúp người dùng hiểu hệ quả của việc từ chối cấp quyền đó. Cụ thể, ứng dụng của bạn phải thông báo cho người dùng về những tính năng sẽ không hoạt động do không có quyền. Khi bạn làm như vậy, lưu ý các phương pháp hay nhất sau:

  • Thu hút sự chú ý của người dùng. Làm nổi bật một phần cụ thể trên giao diện người dùng của ứng dụng trên đó có chức năng bị hạn chế do ứng dụng không có được quyền truy cập cần thiết. Dưới đây là một số ví dụ về những việc bạn có thể làm:

    • Hiển thị thông báo trong đó kết quả hoặc dữ liệu của tính năng có thể xuất hiện.
    • Hiển thị một nút khác chứa biểu tượng lỗi và màu sắc.
  • Vui lòng mô tả cụ thể. Không hiển thị thông báo chung chung. Thay vào đó, hãy làm rõ những tính năng không thể hoạt động vì ứng dụng của bạn không có được quyền cần thiết.

  • Không chặn giao diện người dùng. Nói cách khác, không nên hiển thị thông báo cảnh báo toàn màn hình không cho người dùng tiếp tục dùng ứng dụng của bạn.

Đồng thời, ứng dụng của bạn nên tôn trọng quyết định từ chối cấp quyền của người dùng. Kể từ Android 11 (API cấp 30), nếu người dùng nhiều lần nhấn vào Từ chối với một quyền cụ thể trong suốt thời gian ứng dụng của bạn được cài đặt trên thiết bị, thì người dùng sẽ không xem hộp thoại cấp quyền của hệ thống nếu ứng dụng của bạn yêu cầu cấp lại quyền đó. Hành động của người dùng ngụ ý "không hỏi lại". Trên các phiên bản trước, người dùng sẽ thấy hộp thoại cấp quyền của hệ thống mỗi khi ứng dụng của bạn yêu cầu cấp quyền, trừ khi trước đó họ đã chọn hộp đánh dấu hoặc tuỳ chọn "không hỏi lại".

Nếu người dùng nhiều lần từ chối một yêu cầu quyền, thì hành động này được coi là từ chối vĩnh viễn. Điều quan trọng là chỉ nên nhắc người dùng cấp quyền khi họ cần truy cập vào một tính năng cụ thể, nếu không, bạn có thể vô tình mất khả năng yêu cầu cấp quyền lần nữa.

Trong một số trường hợp nhất định, quyền có thể tự động bị từ chối mà không cần người dùng thực hiện bất kỳ hành động nào. (Một quyền cũng có thể được cấp tự động.) Bạn không nên giả định bất kỳ điều gì về hành vi tự động. Mỗi khi ứng dụng cần truy cập chức năng yêu cầu cấp quyền, hãy kiểm tra để biết ứng dụng vẫn được cấp quyền đó.

Để cung cấp trải nghiệm người dùng chất lượng cao nhất khi yêu cầu cấp quyền cho ứng dụng, bạn cũng nên xem Các phương pháp hay nhất về quyền cho ứng dụng.

Kiểm tra trạng thái từ chối yêu cầu khi kiểm thử và gỡ lỗi

Để xác định xem một ứng dụng có bị từ chối cấp quyền (cho mục đích gỡ lỗi và kiểm thử) vĩnh viễn hay không, hãy sử dụng lệnh sau:

adb shell dumpsys package PACKAGE_NAME

Trong đó PACKAGE_NAME là tên gói cần kiểm tra.

Kết quả của lệnh chứa các phần như sau:

...
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]
...

Quyền truy cập đã bị người dùng từ chối một lần sẽ bị gắn cờ USER_SET. Quyền truy cập đã bị người dùng từ chối hai lần sẽ bị gắn cờ USER_FIXED.

Để đảm bảo người kiểm thử nhìn thấy hộp thoại yêu cầu trong quá trình kiểm thử, hãy đặt lại các cờ này khi bạn gỡ lỗi ứng dụng xong. Để thực hiện việc này, hãy sử dụng lệnh:

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

PERMISSION_NAME là tên của quyền bạn muốn đặt lại.

Để xem danh sách đầy đủ các quyền cho ứng dụng trên Android, hãy truy cập trang Tài liệu tham khảo API về quyền truy cập.

Quyền một lần

Tuỳ chọn có tên &quot;Chỉ lần này&quot; là nút thứ hai trong ba nút trong hộp thoại.
Hình 2. Hộp thoại hệ thống xuất hiện khi ứng dụng yêu cầu cấp quyền một lần.

Kể từ Android 11 (API cấp 30), bất cứ khi nào ứng dụng yêu cầu cấp quyền liên quan đến vị trí, micrô hoặc máy ảnh, hộp thoại cấp quyền, mà người dùng nhìn thấy, chứa tuỳ chọn có tên là Chỉ lần này, như được minh hoạ trong hình 2. Nếu người dùng chọn tuỳ chọn này trong hộp thoại, thì ứng dụng của bạn sẽ tạm thời được cấp quyền một lần.

Sau đó, ứng dụng có thể truy cập vào dữ liệu liên quan trong một khoảng thời gian nhất định, tuỳ thuộc vào hành vi của ứng dụng và hành động của người dùng:

  • Trong khi hoạt động của ứng dụng được hiển thị, thì ứng dụng có thể truy cập dữ liệu.
  • Nếu người dùng chuyển ứng dụng của bạn vào nền, ứng dụng vẫn có thể tiếp tục truy cập dữ liệu trong một khoảng thời gian ngắn.
  • Nếu bạn khởi chạy một dịch vụ trên nền trước trong khi hoạt động được hiển thị, sau đó người dùng chuyển ứng dụng sang chế độ nền, thì ứng dụng của bạn có thể tiếp tục truy cập vào dữ liệu cho đến khi dịch vụ trên nền trước này ngừng hoạt động.

Quá trình xử lý của ứng dụng chấm dứt khi quyền bị thu hồi

Nếu người dùng thu hồi quyền một lần, chẳng hạn như trong phần cài đặt hệ thống, thì ứng dụng của bạn không thể truy cập vào dữ liệu, bất kể bạn đã khởi chạy dịch vụ trên nền trước hay chưa. Cũng như đối với bất kỳ quyền nào, nếu người dùng thu hồi quyền một lần của ứng dụng, thì quy trình của ứng dụng sẽ chấm dứt.

Lần tiếp theo khi người dùng mở ứng dụng của bạn và một tính năng trong ứng dụng yêu cầu quyền truy cập vào thông tin vị trí, micrô hoặc máy ảnh, thì người dùng sẽ nhận được lời nhắc cấp quyền một lần nữa.

Đặt lại quyền không dùng đến

Android cung cấp một số cách để đặt lại quyền không dùng đến khi bắt đầu chạy về trạng thái mặc định, bị từ chối:

Xoá quyền truy cập của ứng dụng

Trên Android 13 (API cấp 33) trở lên, bạn có thể xoá quyền truy cập của ứng dụng vào các quyền khi bắt đầu chạy mà ứng dụng không còn yêu cầu. Khi bạn cập nhật ứng dụng, hãy thực hiện bước này để người dùng có thể dễ hiểu lý do ứng dụng tiếp tục yêu cầu các quyền cụ thể. Kiến thức này giúp người dùng tin tưởng ứng dụng của bạn.

Để xoá quyền truy cập vào một quyền khi bắt đầu chạy, hãy chuyển tên của quyền đó vào revokeSelfPermissionOnKill(). Để xoá quyền truy cập vào một nhóm các quyền khi bắt đầu chạy, hãy chuyển một tập hợp tên quyền vào revokeSelfPermissionsOnKill(). Quy trình xoá quyền diễn ra không đồng bộ và loại bỏ tất cả các quy trình liên kết với UID của ứng dụng.

Để hệ thống xoá quyền truy cập của ứng dụng vào các quyền, mọi quy trình liên kết với ứng dụng đều phải bị loại bỏ. Khi bạn gọi API, hệ thống sẽ xác định thời điểm an toàn để loại bỏ các quy trình này. Thông thường, hệ thống sẽ chờ cho đến khi ứng dụng dành một khoảng thời gian dài để chạy trong nền thay vì trong nền trước.

Để thông báo cho người dùng rằng ứng dụng của bạn không còn yêu cầu truy cập vào các quyền cụ thể khi bắt đầu chạy, hãy hiển thị một hộp thoại vào lần tới người dùng chạy ứng dụng. Hộp thoại này có thể bao gồm danh sách các quyền.

Tự động đặt lại quyền cho các ứng dụng không dùng đến

Nếu ứng dụng của bạn nhắm mục tiêu vào Android 11 (API cấp 30) trở lên và không được dùng trong vài tháng, thì hệ thống sẽ bảo vệ dữ liệu người dùng bằng cách tự động đặt lại các quyền nhạy cảm khi bắt đầu chạy mà người dùng đã cấp cho ứng dụng. Hãy tìm hiểu thêm trong hướng dẫn về trạng thái ngủ đông của ứng dụng.

Yêu cầu để trở thành trình xử lý mặc định nếu cần

Một số ứng dụng phụ thuộc vào quyền truy cập thông tin nhạy cảm của người dùng liên quan đến nhật ký cuộc gọi và tin nhắn SMS. Nếu muốn yêu cầu cấp quyền cụ thể với nhật ký cuộc gọi và tin nhắn SMS cũng như xuất bản ứng dụng lên Cửa hàng Play, thì bạn phải nhắc người dùng đặt ứng dụng làm trình xử lý mặc định cho một hàm hệ thống cốt lõi trước khi yêu cầu quyền khi bắt đầu chạy này.

Để biết thêm thông tin về trình xử lý mặc định, bao gồm cả hướng dẫn về hiển thị lời nhắc trình xử lý mặc định cho người dùng, hãy xem hướng dẫn về các quyền chỉ được sử dụng trong trình xử lý mặc định.

Cấp tất cả quyền khi bắt đầu chạy cho mục đích kiểm thử

Để tự động cấp tất cả các quyền khi bắt đầu chạy khi bạn cài đặt một ứng dụng trên trình mô phỏng hoặc thiết bị kiểm thử, sử dụng tuỳ chọn -g cho lệnh adb shell install như minh hoạ trong đoạn mã sau đây:

adb shell install -g PATH_TO_APK_FILE

Tài nguyên khác

Để biết thêm thông tin về cấp quyền, đọc những bài viết sau:

Để tìm hiểu thêm về cách yêu cầu cấp quyền, hãy xem mẫu về quyền

Bạn cũng có thể hoàn thành lớp học lập trình này để minh hoạ các phương pháp hay nhất về quyền riêng tư.