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 quản lý quyền giúp đơn giản hoá quy 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 đến 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 bạn hoạt động như mong đợi 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 trong thời gian chạy mà người dùng có thể chọn từ chối riêng biệt với những quyền mà ứng dụng của bạn yêu cầu cụ thể. Dịch vụ Google Play tự động lấy tất cả các quyền cần thiết để hỗ trợ các API của dịch vụ này. Tuy nhiên, ứng dụng của bạn vẫn nên kiểm tra và yêu cầu cấp quyền khi bắt đầu chạy nếu cần, đồng thời xử lý 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 một quyền cần thiết cho 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 các 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 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 bắt buộc trước khi gọi một API. Bạn có thể thực hiện việc này bằ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 một 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 phải triển khai điều 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 duy nhất.

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 cấp quyền

Nếu các quyền mà ứng dụng của bạn 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 đã từ chối yêu cầu cấp quyền trước đó, thì ứng dụng của bạn phải hiển thị thêm nội dung giải thích trước khi yêu cầu cấp quyền một lần nữa. Nhiều khả năng người dùng sẽ cấp quyền khi họ hiểu được lý do cần có quyền đó và lợi ích tức thì mà họ nhận được.

Trong trường hợp này, trước khi gọi requestPermissions, bạn nên gọi shouldShowRequestPermissionRationale. Nếu phương thức này trả về giá trị true, bạn nên tạo một số giao diện người dùng để hiển thị thêm ngữ cảnh 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 của Dịch vụ Google Play sẽ tự động hiển thị một hộp thoại (nếu ứng dụng được khởi tạo bằng Activity) hoặc thông báo trên khay hệ thống (nếu ứng dụng được khởi tạo 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 bạn cấp quyền.