הרשאות בזמן ריצה ושירותי Google Play

החל מגרסת Android 6.0 Marshmallow, ב-Android נעשה שימוש במודל הרשאות שמייעל את תהליך ההתקנה והעדכון האוטומטי של האפליקציה. ההרשאות נדרשות בזמן הריצה ולא לפני ההתקנה של האפליקציה. בנוסף, המשתמשים יכולים לבחור לדחות הרשאות ספציפיות. כדי לתת למשתמשים את הגמישות הזו, צריך לוודא שהאפליקציה פועלת כמצופה כשמשתמש מפעיל או משבית הרשאה ספציפית.

ב-Google Play Services יש הרשאות בתחילת ההפעלה שהמשתמשים יכולים לדחות, בנפרד מההרשאות שהאפליקציה שלכם מבקשת באופן ספציפי. תוכנת Google Play Services מקבלת באופן אוטומטי את כל ההרשאות הנדרשות כדי לתמוך בממשקי ה-API שלה. עם זאת, האפליקציה עדיין צריכה לבדוק ולבקש הרשאות בזמן הריצה לפי הצורך, ולטפל בשגיאות בצורה מתאימה במקרים שבהם המשתמש דחה את ההרשאה הנדרשת ל-API שבו האפליקציה משתמשת ב-Google Play Services.

מומלץ להגדיר את הציפיות של המשתמשים בזמן הריצה. השיטות המומלצות הבאות יעזרו לכם למנוע בעיות פוטנציאליות.

דרישות מוקדמות

עליך להצהיר על הרשאות בקובץ AndroidManifest.xml. לדוגמה:

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

הנחיות

אימות ההרשאות לפני קריאה ל-API

אחרי שמצהירים על ממשקי ה-API שאתם רוצים להשתמש בהם בקובץ AndroidManifest.xml, חשוב לוודא שיש לכם את ההרשאה הנדרשת לפני שמפעילים את ה-API. אפשר לעשות זאת באמצעות השיטה checkSelfPermission של ActivityCompat או ContextCompat.

אם השיחה מחזירה את הערך False, המשמעות היא שההרשאות לא ניתנו וצריך להשתמש ב-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();
}

הטמעת הקריאה החוזרת (callback) של הרשאת הבקשה

אם המשתמש לא נתן לאפליקציה את ההרשאות הנדרשות, צריך להפעיל את השיטה requestPermissions כדי לבקש מהמשתמש להעניק אותה. התגובה מהמשתמש מתועדת בקריאה החוזרת (callback) של 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. אם הערך מחזיר TRUE, צריך ליצור ממשק משתמש כדי להציג הקשר נוסף של ההרשאה.

לדוגמה, הקוד עשוי להיראות כך:

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 Services מוודא שיש לו את כל ההרשאות הנדרשות. connect() נכשלת כשחסרות קבוצות ההרשאות שנדרשות ל-Google Play Services.

אם הקריאה ל-connect() נכשלת, צריך לוודא שהאפליקציה מטפלת בכשל בחיבור בצורה נכונה. אם ל-Google Play Services חסרים הרשאות, אתם יכולים להפעיל את 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) שהמשתמשים יכולים להקיש עליהן כדי להפעיל את ה-Intent לפענוח ההרשאות. אחרי שההרשאה תוענק, קריאות יצורפו לתור וינסו שוב.