Dịch vụ Google Play và quyền khi bắt đầu chạy

Kể từ Android 6.0 Marshmallow, Android sử dụng một mô hình quyền giúp đơn giản hoá quá trình cài đặt ứng dụng và tự động cập nhật. Các quyền được yêu cầu trong thời gian chạy thay vì trước khi cài đặt ứng dụng. Ngoài ra, người dùng có thể chọn từ chối một số quyền cụ thể. Để mang lại cho người dùng sự linh hoạt này, bạn cần đảm bảo rằng ứng dụng của mình hoạt động như dự kiến khi người dùng bật hoặc tắt một quyền cụ thể.

Bản thân Dịch vụ Google Play có các quyền khi bắt đầu chạy mà người dùng có thể chọn từ chối riêng biệt với các quyền mà ứng dụng của bạn yêu cầu cụ thể. Dịch vụ Google Play sẽ tự động lấy tất cả các quyền cần thiết để hỗ trợ các API của mình. Tuy nhiên, ứng dụng của bạn vẫn phải kiểm tra và yêu cầu quyền khi bắt đầu chạy khi cần thiết, đồng thời xử lý các lỗi một cách thích hợp trong trường hợp người dùng từ chối cấp cho Dịch vụ Google Play quyền cần thiết cho một API mà ứng dụng của bạn sử dụng.

Bạn nên quản lý kỳ vọng của người dùng trong việc thiết lập các quyền mà thời gian chạy có thể yêu cầu. Các phương pháp hay nhất sau đây sẽ giúp bạn tránh được các vấn đề tiềm ẩn.

Điều kiện tiên quyết

Bạn cần khai báo quyền trong tệp AndroidManifest.xml. Ví dụ:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Nguyên tắc

Xác minh quyền trước khi gọi API

Sau khi bạn khai báo các API mà bạn muốn sử dụng trong tệp AndroidManifest.xml, hãy xác minh rằng bạn có quyền cần thiết trước khi gọi API. Bạn có thể thực hiện việc này bằng cách sử dụng phương thức checkSelfPermission của ActivityCompat hoặc ContextCompat.

Nếu lệnh gọi trả về giá trị false, tức là các quyền chưa được cấp và bạn nên sử dụng requestPermissions để yêu cầu các quyền đó. Phản hồi cho yêu cầu này sẽ được trả về trong lệnh gọi lại mà bạn sẽ thấy trong bước tiếp theo.

Ví dụ:

Kotlin

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            // Request Permissions Now
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
    } else {
        // permission has been granted, continue as usual
        val locationResult = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }

Java

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Request Permissions Now
        ActivityCompat.requestPermissions(
            this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            REQUEST_LOCATION_PERMISSION_CODE);
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }

Triển khai lệnh gọi lại yêu cầu cấp quyền

Nếu người dùng chưa cấp quyền mà ứng dụng của bạn cần, bạn nên gọi phương thức requestPermissions để yêu cầu người dùng cấp quyền. Phản hồi của người dùng được ghi lại trong lệnh gọi lại onRequestPermissionsResult. Ứng dụng của bạn nên triển khai tính năng này và luôn kiểm tra các giá trị trả về vì yêu cầu có thể bị từ chối hoặc huỷ. Bạn cũng có thể yêu cầu và kiểm tra nhiều quyền cùng một lúc – mẫu sau đây chỉ kiểm tra một quyền.

Kotlin

    fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if (grantResults.singleOrNull() == PackageManager.PERMISSION_GRANTED) {
               // We can now safely use the API we requested access to
               val locationResult: Task = LocationServices
                    .getFusedLocationProviderClient(this /* Context */)
                    .lastLocation // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }

Java

    public void onRequestPermissionsResult(int requestCode,
                                            String[] permissions,
                                            int[] grantResults) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if(grantResults.length == 1
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // We can now safely use the API we requested access to
                Task locationResult = LocationServices
                    .getFusedLocationProviderClient(this /** Context */)
                    .getLastLocation(); // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }

Hiện lý do yêu cầu cấp quyền

Nếu các quyền mà ứng dụng yêu cầu là cần thiết cho các tính năng cốt lõi của ứng dụng và người dùng trước đó đã từ chối yêu cầu cấp quyền, thì ứng dụng của bạn sẽ hiển thị thêm nội dung giải thích trước khi yêu cầu cấp quyền lại. Người dùng có nhiều khả năng cấp quyền khi họ hiểu lý do cần có quyền và lợi ích tức thì cho họ.

Trong trường hợp này, trước khi gọi requestPermissions, bạn nên gọi shouldShowRequestPermissionRationale. Nếu giá trị trả về là đúng, bạn nên tạo một số giao diện người dùng để hiển thị ngữ cảnh bổ sung cho quyền đó.

Ví dụ: mã của bạn có thể có dạng như sau:

Kotlin

    private const val REQUEST_LOCATION_PERMISSION_CODE = 2
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        if ActivityCompat.shouldShowRequestPermissionRationale(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) {
            // Display UI and wait for user interaction
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
        }
    } else {
        // Permission has already been granted, continue as usual
        val locationResult: Task = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }

Java

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        private static final int REQUEST_LOCATION_PERMISSION_CODE = 2;
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {
            // Display UI and wait for user interaction
        } else {
            ActivityCompat.requestPermissions(
                this, new String[]{Manifest.permission.LOCATION_FINE},
                REQUEST_LOCATION_PERMISSION_CODE);
        }
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }

Các lệnh gọi API Dịch vụ Google Play sẽ tự động hiển thị một hộp thoại (nếu ứng dụng được tạo bản sao bằng Activity) hoặc thông báo trên khay hệ thống (nếu ứng dụng được tạo bản sao bằng Context) mà người dùng có thể nhấn vào để bắt đầu ý định phân giải quyền. Các lệnh gọi sẽ được đưa vào hàng đợi và thử lại sau khi quyền được cấp.