บริการ Google Play และสิทธิ์รันไทม์

ตั้งแต่ Android 6.0 Marshmallow ทาง Android ใช้ รูปแบบสิทธิ์ที่ช่วยปรับปรุงการติดตั้งแอปและ การอัปเดตอัตโนมัติ มีการขอสิทธิ์ระหว่างรันไทม์แทนที่จะเป็นก่อน การติดตั้งแอป นอกจากนี้ ผู้ใช้ยังเลือกที่จะปฏิเสธสิทธิ์บางอย่างได้ด้วย เพื่อให้ผู้ใช้มีความยืดหยุ่นเช่นนี้ คุณต้องตรวจสอบว่าแอปของคุณมีลักษณะการทำงาน จะมีผลเมื่อผู้ใช้เปิดหรือปิดสิทธิ์บางอย่าง

บริการ Google Play เองมีสิทธิ์รันไทม์ที่ผู้ใช้เลือกได้ ปฏิเสธแยกต่างหากจากการอนุญาตที่คำขอเฉพาะ แอปพลิเคชัน บริการ Google Play จะได้รับสิทธิ์ทั้งหมดที่จำเป็นโดยอัตโนมัติ เพื่อรองรับ API อย่างไรก็ตาม แอปก็ยังควรตรวจสอบและขอรันไทม์ ตามความเหมาะสมและจัดการกับข้อผิดพลาดอย่างเหมาะสมในกรณีที่ผู้ใช้ ได้ปฏิเสธสิทธิ์ที่จำเป็นสำหรับ API ที่แอปของคุณใช้กับบริการ Google Play

คุณควรจัดการความคาดหวังของผู้ใช้ในการกำหนดสิทธิ์ที่ ที่รันไทม์อาจจำเป็นต้องใช้ แนวทางปฏิบัติแนะนำต่อไปนี้จะช่วยให้คุณหลีกเลี่ยง ปัญหาที่อาจเกิดขึ้น

ข้อกำหนดเบื้องต้น

คุณจะต้องประกาศสิทธิ์ในไฟล์ AndroidManifest.xml เช่น

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

หลักเกณฑ์

ยืนยันสิทธิ์ก่อนเรียกใช้ API

เมื่อคุณประกาศ API ที่ต้องการใช้ใน AndroidManifest.xml ไฟล์ โปรดตรวจสอบว่าคุณมีสิทธิ์ที่จำเป็น ก่อนเรียกใช้ API ซึ่งทำได้โดยใช้เมธอด checkSelfPermission ของ ActivityCompat หรือ ContextCompat

หากการเรียกแสดงค่า "เท็จ" แสดงว่าไม่มีการให้สิทธิ์และคุณ ควรใช้ requestPermissions เพื่อส่งคำขอ การตอบสนองต่อกรณีนี้คือ แสดงใน Callback ซึ่งคุณจะเห็นในขั้นตอนถัดไป

เช่น

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();
}

ใช้ Callback เกี่ยวกับสิทธิ์คำขอ

หากผู้ใช้ไม่ได้ให้สิทธิ์ที่แอปของคุณต้องการ ควรเรียกเมธอด requestPermissions เพื่อขอการเรียก ให้ผู้ใช้ให้สิทธิ์ ระบบจะบันทึกคำตอบจากผู้ใช้ไว้ในส่วน onRequestPermissionsResult ติดต่อกลับ แอปของคุณควร ให้ใช้สิ่งนี้และตรวจสอบมูลค่า การแสดงผลเสมอเนื่องจากคำขออาจ ปฏิเสธหรือยกเลิก คุณยังขอและตรวจสอบสิทธิ์หลายรายการได้ที่ เพียงครั้งเดียว ตัวอย่างต่อไปนี้จะตรวจหาเฉพาะสิทธิ์เดียวเท่านั้น

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
        }
    }
}

แสดงเหตุผลของสิทธิ์

หากสิทธิ์ที่แอปของคุณขอจำเป็นสำหรับฟีเจอร์หลักของ และผู้ใช้เคยปฏิเสธคำขอสิทธิ์ก่อนหน้านี้ แอปของคุณควร แสดงคำอธิบายเพิ่มเติมก่อนขอสิทธิ์อีกครั้ง ผู้ใช้ มีแนวโน้มที่จะให้สิทธิ์มากขึ้นเมื่อเข้าใจเหตุผลในการให้สิทธิ์ เป็นสิ่งจำเป็นและจะได้รับประโยชน์โดยทันที

ในกรณีนี้ ก่อนโทรหา requestPermissions คุณควรโทรหา shouldShowRequestPermissionRationale หากส่งคืน จริง คุณควรสร้าง UI บางอย่างเพื่อแสดงบริบทเพิ่มเติมสำหรับฟิลด์ สิทธิ์

ตัวอย่างเช่น โค้ดของคุณอาจมีลักษณะดังนี้

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();
}

จัดการความล้มเหลวในการเชื่อมต่อ

หากแอปใช้ GoogleApiClient ที่เลิกใช้งานแล้ว เมื่อคุณเรียกใช้ connect() บริการ Google Play จะตรวจสอบว่า สิทธิ์ที่จำเป็น connect() ล้มเหลวเมื่อมีกลุ่มสิทธิ์ใดก็ได้ ที่บริการ Google Play ต้องการ

หากโทรหา connect() ไม่สำเร็จ โปรดตรวจสอบว่าแอปเป็นผู้จัดการ การเชื่อมต่อล้มเหลวอย่างถูกต้อง หากบริการ Google Play ตัวเองไม่มีสิทธิ์ คุณสามารถเรียกใช้ startResolutionForResult() เพื่อ เริ่มขั้นตอนการใช้งานของผู้ใช้เพื่อแก้ไข

เช่น

@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;
    }
}

การเรียก API แบบ GoogleApi ที่ใหม่กว่าจะแสดงกล่องโต้ตอบโดยอัตโนมัติ (หาก ไคลเอ็นต์จะสร้างอินสแตนซ์ด้วย Activity) หรือการแจ้งเตือนในถาดระบบ (หาก ไคลเอ็นต์จะสร้างอินสแตนซ์ด้วย Context) ซึ่งผู้ใช้สามารถแตะเพื่อเริ่ม ความตั้งใจในการแก้ปัญหาสิทธิ์ การโทรจะต้องมีการจัดคิวและลองใหม่เมื่อ ได้รับสิทธิ์