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

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

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

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

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

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

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

หลักเกณฑ์

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

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

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

เช่น

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

ติดตั้งใช้งานโค้ดเรียกกลับเพื่อขอสิทธิ์

หากผู้ใช้ไม่ได้ให้สิทธิ์ที่แอปต้องการ ควรเรียกใช้เมธอด 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) ที่ผู้ใช้แตะเพื่อเริ่มจุดประสงค์การแก้ไขปัญหาสิทธิ์ได้ ระบบจะจัดคิวการโทรและลองใหม่เมื่อได้รับสิทธิ์แล้ว