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