Kể từ Android 6.0 Marshmallow, Android sử dụng mô hình quản lý quyền đơn giản hoá quy trình cài đặt ứng dụng và tự động cập nhật. Quyền được yêu cầu trong thời gian chạy thay vì trước đó lượt 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 sự linh hoạt này cho người dùng, 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ũng 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 do . Dịch vụ Google Play tự động có được mọi quyền cần thiết để hỗ trợ các API. Tuy nhiên, ứng dụng của bạn vẫn cần kiểm tra và yêu cầu thời gian chạy quyền khi cần thiết và 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 quyền cần thiết cho Dịch vụ Google Play đối với API mà ứng dụng của bạn sử dụng.
Một phương pháp hay là quản lý kỳ vọng của người dùng khi đặt quyền 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 vấn đề tiềm ẩn.
Điều kiện tiên quyết
Bạn sẽ 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
AndroidManifest.xml
, hãy đảm bảo rằng bạn có quyền cần thiết
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
trong số ActivityCompat
hoặc ContextCompat
.
Nếu lệnh gọi trả về giá trị "false", điều này có nghĩa là quyền chưa được cấp và bạn
nên sử dụng requestPermissions
để yêu cầu các quyền đó. Câu trả lời cho vấn đề này là
được trả về trong một lệnh gọi lại mà bạn sẽ thấy ở bước tiếp theo.
Ví dụ:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Check Permissions Now
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_LOCATION);
} else {
// permission has been granted, continue as usual
Task<Location> locationResult = LocationServices
.getFusedLocationProviderClient(this /** Context */)
.getLastLocation();
}
Triển khai lệnh gọi lại quyền yêu cầu
Nếu người dùng chưa cấp quyền mà ứng dụng của bạn cần,
Phương thức requestPermissions
nên được gọi để 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 sẽ
hãy triển khai lệnh 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 bị huỷ. Bạn cũng có thể yêu cầu và kiểm tra nhiều quyền tại
một lần--mẫu sau đây chỉ kiểm tra một quyền duy nhất.
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults) {
if (requestCode == REQUEST_LOCATION) {
if(grantResults.length == 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// We can now safely use the API we requested access to
Task<Location> locationResult = LocationServices
.getFusedLocationProviderClient(this /** Context */)
.getLastLocation();
} else {
// Permission was denied or request was cancelled
}
}
}
Giải thích lý do yêu cầu 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 và người dùng đã từ chối yêu cầu quyền trước đó, thì ứng dụng của bạn phải hãy hiện nội dung giải thích bổ sung trước khi yêu cầu cấp lại quyền. Người dùng có nhiều khả năng cấp quyền hơn khi họ hiểu lý do quyền đó là cần thiết và mang lại 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 trả về
true, 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ể trông giống như sau:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Check Permissions Now
private static final int REQUEST_LOCATION = 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},
ACCESS_FINE_LOCATION);
}
} else {
// permission has been granted, continue as usual
Task<Location> locationResult = LocationServices
.getFusedLocationProviderClient(this /** Context */)
.getLastLocation();
}
Xử lý lỗi kết nối
Nếu ứng dụng của bạn sử dụng GoogleApiClient
không dùng nữa, khi bạn gọi
connect()
, Dịch vụ Google Play xác thực rằng ứng dụng này có tất cả
các quyền cần thiết. connect()
sẽ không hoạt động được khi có bất kỳ nhóm quyền nào
các dịch vụ Google Play cần thiết.
Nếu lệnh gọi đến connect()
không thành công, hãy đảm bảo ứng dụng của bạn xử lý
lỗi kết nối đúng cách. Nếu Dịch vụ Google Play
bản thân nó thiếu quyền, bạn có thể gọi startResolutionForResult()
để
bắt đầu luồng người dùng để khắc phục chúng.
Ví dụ:
@Override
public void onConnectionFailed(ConnectionResult result) {
if (mResolvingError) {
// Already attempting to resolve an error.
return;
} else if (result.hasResolution()) {
try {
mResolvingError = true;
result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
} catch (SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect();
}
} else {
// Show dialog using GooglePlayServicesUtil.getErrorDialog()
showErrorDialog(result.getErrorCode());
mResolvingError = true;
}
}
Các lệnh gọi API mới hơn dựa trên GoogleApi
sẽ tự động hiển thị một hộp thoại (nếu
máy khách được tạo thực thể bằng Activity
) hoặc thông báo khay hệ thống (nếu
máy khách được tạo thực thể bằng Context
) mà người dùng có thể nhấn để bắt đầu
ý định phân giải quyền. Các cuộc gọi sẽ được thêm vào hàng đợi và được thử lại sau
quyền đã được cấp.