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 TasklocationResult = 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 TasklocationResult = 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 TasklocationResult = 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.