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

כל אפליקציית Android פועלת בארגז חול עם גישה מוגבלת. אם האפליקציה שלכם צריכה להשתמש במשאבים או במידע מחוץ לארגז החול שלה, אתם יכולים להצהיר על הרשאת זמן ריצה ולהגדיר בקשת הרשאה שתספק את הגישה הזו. השלבים האלה הם חלק מתהליך העבודה לשימוש בהרשאות.

אם הצהרתם על הרשאות מסוכנות, והאפליקציה שלכם מותקנת במכשיר עם Android מגרסה 6.0 (רמת API‏ 23) ואילך, אתם צריכים לבקש את ההרשאות המסוכנות בזמן הריצה. לשם כך, צריך לפעול לפי השלבים במדריך הזה.

אם לא הצהרתם על הרשאות מסוכנות, או אם האפליקציה שלכם מותקנת במכשיר עם Android 5.1 (רמת API‏ 22) או גרסה מוקדמת יותר, ההרשאות ניתנות באופן אוטומטי ואין צורך לבצע את השלבים שנותרו בדף הזה.

עקרונות בסיסיים

העקרונות הבסיסיים לבקשת הרשאות בזמן ריצה הם:

  • לבקש הרשאה בהקשר, כשהמשתמש מתחיל לקיים אינטראקציה עם התכונה שדורשת אותה.
  • לא לחסום את המשתמש. תמיד צריך לספק אפשרות לבטל תהליך של ממשק משתמש חינוכי, כמו תהליך שמסביר את הנימוק לבקשת הרשאות.
  • אם המשתמש דוחה או מבטל הרשאה שנדרשת לתכונה מסוימת, צריך לבצע הפחתה חיננית (graceful degradation) של האפליקציה כדי שהמשתמש יוכל להמשיך להשתמש בה. אפשר לעשות זאת על ידי השבתת התכונה שדורשת את ההרשאה.
  • אל תניחו הנחות לגבי התנהגות המערכת. לדוגמה, אל תניחו שההרשאות מופיעות באותה קבוצת הרשאות. קבוצת הרשאות רק עוזרת למערכת לצמצם את מספר תיבות הדו-שיח של המערכת שמוצגות למשתמשים כשאפליקציה מבקשת הרשאות שקשורות זו לזו.

תהליך העבודה לבקשת הרשאות

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

אם הגעת למסקנה שהאפליקציה שלך צריכה להצהיר על הרשאות ולבקש אותן בזמן הריצה, צריך לבצע את השלבים הבאים:

  1. מצהירים על ההרשאות שהאפליקציה עשויה לבקש בקובץ המניפסט של האפליקציה.
  2. מעצבים את חוויית המשתמש באפליקציה כך שפעולות ספציפיות ישויכו להרשאות ספציפיות בזמן ריצה. חשוב להודיע למשתמשים אילו פעולות עשויות לדרוש שהם ייתנו לאפליקציה הרשאת גישה לנתונים פרטיים.
  3. מחכים שהמשתמש יפעיל את המשימה או הפעולה באפליקציה שדורשת גישה לנתונים פרטיים ספציפיים. בשלב הזה, האפליקציה יכולה לבקש הרשאת זמן ריצה שנדרשת כדי לגשת לנתונים האלה.
  4. בודקים אם המשתמש כבר נתן את הרשאת זמן הריצה שהאפליקציה דורשת. אם כן, האפליקציה יכולה לגשת לנתונים הפרטיים של המשתמש. אם הבעיה ממשיכה, עוברים לשלב הבא.

    בכל פעם שמבצעים פעולה שנדרשת לה הרשאה, צריך לבדוק אם ההרשאה הזו כבר ניתנה.

  5. בודקים אם האפליקציה צריכה להציג למשתמש נימוק שמסביר למה האפליקציה צריכה שהמשתמש יעניק לה הרשאה מסוימת בזמן הריצה. אם המערכת קובעת שלא צריך להציג הסבר באפליקציה, היא ממשיכה ישירות לשלב הבא בלי להציג רכיב ממשק משתמש.

    אם המערכת קובעת שהאפליקציה צריכה להציג נימוק, צריך להציג אותו למשתמש ברכיב של ממשק המשתמש. בנימוק הזה, צריך להסביר בצורה ברורה לאילו נתונים האפליקציה מנסה לגשת, ואילו יתרונות היא יכולה לתת למשתמש אם הוא יעניק לה את ההרשאה בתחילת ההפעלה. אחרי שהמשתמש מאשר שהוא הבין את הנימוק, ממשיכים לשלב הבא.

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

  7. בודקים את התשובה של המשתמש – אם הוא בחר להעניק או לדחות את הרשאת זמן הריצה.

  8. אם המשתמש העניק הרשאה לאפליקציה, היא תוכל לגשת לנתונים הפרטיים שלו. אם המשתמש דחה את ההרשאה, צריך לבצע הפחתה חיננית (graceful degradation) של חוויית השימוש באפליקציה ולשמור על הפונקציונליות שלא תלויה בנתונים שאין גישה אליהם.

איור 1 מציג את תהליך העבודה ואת קבוצת ההחלטות שקשורות לתהליך הזה:

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

בודקים אם האפליקציה כבר קיבלה את ההרשאה

כדי לבדוק אם המשתמש כבר נתן לאפליקציה הרשאה מסוימת, מעבירים את ההרשאה הזו לשיטה [ContextCompat.checkSelfPermission()][12]. השיטה הזו מחזירה את הערך PERMISSION_GRANTED או את הערך PERMISSION_DENIED, בהתאם להרשאה שיש לאפליקציה.

הסבר למה האפליקציה צריכה את ההרשאה

בתיבת הדו-שיח של ההרשאות שמוצגת על ידי המערכת כשמתקשרים אל [requestPermissions()][15] מצוין איזו הרשאה האפליקציה רוצה, אבל לא מצוין למה. במקרים מסוימים, המשתמשים עשויים להתבלבל. מומלץ להסביר למשתמש למה האפליקציה רוצה את ההרשאות לפני שמפעילים את requestPermissions().

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

במקרים מסוימים, כדאי גם ליידע את המשתמשים בזמן אמת על גישה לנתונים רגישים. לדוגמה, אם אתם ניגשים למצלמה או למיקרופון, כדאי להודיע על כך למשתמש באמצעות סמל התראה איפשהו באפליקציה או במגש ההתראות (אם האפליקציה פועלת ברקע), כדי שלא ייראה כאילו אתם אוספים נתונים בהיחבא.

אם אתם צריכים לבקש הרשאה כדי להפעיל תכונה מסוימת, אבל הסיבה לכך לא ברורה למשתמש, אתם צריכים להסביר למה אתם צריכים הרשאות רגישות.

אם השיטה ContextCompat.checkSelfPermission() מחזירה PERMISSION_DENIED, צריך להתקשר אל [shouldShowRequestPermissionRationale()][16]. אם השיטה הזו מחזירה true, צריך להציג למשתמש ממשק משתמש עם הסבר. בממשק המשתמש הזה, צריך לתאר למה התכונה שהמשתמש רוצה להפעיל צריכה הרשאה מסוימת.

בנוסף, אם האפליקציה מבקשת הרשאה שקשורה למיקום, למיקרופון או למצלמה, כדאי להסביר למה האפליקציה צריכה גישה למידע הזה.

שליחת בקשה להרשאות

אחרי שהמשתמש צפה בממשק משתמש עם הסבר, או אם ערך ההחזרה של shouldShowRequestPermissionRationale() מציין שלא צריך להציג ממשק משתמש עם הסבר, מבקשים את ההרשאה. למשתמשים מוצג דו-שיח של הרשאת מערכת, שבו הם יכולים לבחור אם להעניק לאפליקציה הרשאה מסוימת.

כדי לעשות זאת, משתמשים בחוזה RequestPermission, שכלול בספריית AndroidX, שבו מאפשרים למערכת לנהל את קוד בקשת ההרשאה בשבילכם. השימוש בחוזה RequestPermission מפשט את הלוגיקה, ולכן מומלץ להשתמש בו כשאפשר. עם זאת, אם צריך, אפשר גם לנהל קוד בקשה בעצמך כחלק מבקשת ההרשאה, ולכלול את קוד הבקשה הזה בלוגיקה של הקריאה החוזרת (callback) של ההרשאה.

ההגדרה הזו מאפשרת למערכת לנהל את קוד בקשת ההרשאה

כדי לאפשר למערכת לנהל את קוד הבקשה שמשויך לבקשת הרשאות, מוסיפים תלויות בספריות הבאות בקובץ build.gradle של המודול:

אחר כך תוכלו להשתמש באחת מהמחלקות הבאות:

בשלבים הבאים מוסבר איך להשתמש בחוזה RequestPermission. התהליך כמעט זהה לחוזה RequestMultiplePermissions.

  1. בלוגיקה של הפעילות או של קטע הקוד, מעבירים הטמעה של ActivityResultCallback לקריאה אל registerForActivityResult(). התג ActivityResultCallback מגדיר איך האפליקציה מטפלת בתשובת המשתמש לבקשת ההרשאה.

    שומרים הפניה לערך ההחזרה של registerForActivityResult(), שהוא מסוג ActivityResultLauncher.

  2. כדי להציג את תיבת הדו-שיח של הרשאות המערכת כשצריך, קוראים לשיטה launch() במופע של ActivityResultLauncher ששמרתם בשלב הקודם.

    אחרי שמתבצעת קריאה ל-launch(), מופיעה תיבת הדו-שיח של הרשאות המערכת. כשהמשתמש בוחר באפשרות, המערכת מפעילה באופן אסינכרוני את ההטמעה של ActivityResultCallback שהגדרתם בשלב הקודם.

    הערה: אי אפשר להתאים אישית את תיבת הדו-שיח שמופיעה באפליקציה כשמתקשרים אל launch(). כדי לספק למשתמש מידע נוסף או הקשר, צריך לשנות את ממשק המשתמש של האפליקציה כך שיהיה למשתמשים קל יותר להבין למה תכונה מסוימת באפליקציה דורשת הרשאה מסוימת. לדוגמה, אפשר לשנות את הטקסט שמופיע בלחצן להפעלת התכונה.

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

בקטע הקוד הבא מוצג אופן הטיפול בתגובה להרשאות:

Kotlin

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // You can use the API that requires the permission.
        performAction(...)
    }
    ActivityCompat.shouldShowRequestPermissionRationale(
            this, Manifest.permission.REQUESTED_PERMISSION) -> {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected, and what
        // features are disabled if it's declined. In this UI, include a
        // "cancel" or "no thanks" button that lets the user continue
        // using your app without granting the permission.
        showInContextUI(...)
    }
    else -> {
        // You can directly ask for the permission.
        requestPermissions(CONTEXT,
                arrayOf(Manifest.permission.REQUESTED_PERMISSION),
                REQUEST_CODE)
    }
}

Java

if (ContextCompat.checkSelfPermission(
        CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
        PackageManager.PERMISSION_GRANTED) {
    // You can use the API that requires the permission.
    performAction(...);
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
        this, Manifest.permission.REQUESTED_PERMISSION)) {
    // In an educational UI, explain to the user why your app requires this
    // permission for a specific feature to behave as expected, and what
    // features are disabled if it's declined. In this UI, include a
    // "cancel" or "no thanks" button that lets the user continue
    // using your app without granting the permission.
    showInContextUI(...);
} else {
    // You can directly ask for the permission.
    requestPermissions(CONTEXT,
            new String[] { Manifest.permission.REQUESTED_PERMISSION },
            REQUEST_CODE);
}

קטע הקוד הבא מדגים את התהליך המומלץ לבדיקת הרשאה ולבקשת הרשאה מהמשתמש כשצריך:

Kotlin

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // You can use the API that requires the permission.
    }
    ActivityCompat.shouldShowRequestPermissionRationale(
            this, Manifest.permission.REQUESTED_PERMISSION) -> {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected, and what
        // features are disabled if it's declined. In this UI, include a
        // "cancel" or "no thanks" button that lets the user continue
        // using your app without granting the permission.
        showInContextUI(...)
    }
    else -> {
        // You can directly ask for the permission.
        // The registered ActivityResultCallback gets the result of this request.
        requestPermissionLauncher.launch(
                Manifest.permission.REQUESTED_PERMISSION)
    }
}

Java

if (ContextCompat.checkSelfPermission(
        CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
        PackageManager.PERMISSION_GRANTED) {
    // You can use the API that requires the permission.
    performAction(...);
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
        this, Manifest.permission.REQUESTED_PERMISSION)) {
    // In an educational UI, explain to the user why your app requires this
    // permission for a specific feature to behave as expected, and what
    // features are disabled if it's declined. In this UI, include a
    // "cancel" or "no thanks" button that lets the user continue
    // using your app without granting the permission.
    showInContextUI(...);
} else {
    // You can directly ask for the permission.
    // The registered ActivityResultCallback gets the result of this request.
    requestPermissionLauncher.launch(
            Manifest.permission.REQUESTED_PERMISSION);
}

ניהול קוד בקשת ההרשאה באופן עצמאי

במקום לאפשר למערכת לנהל את קוד בקשת ההרשאה, אתם יכולים לנהל את קוד בקשת ההרשאה בעצמכם. כדי לעשות זאת, צריך לכלול את קוד הבקשה בשיחה אל [requestPermissions()][15].

בקטע הקוד הבא מוצג איך לבקש הרשאה באמצעות קוד בקשה:

Kotlin

when {
    ContextCompat.checkSelfPermission(
            CONTEXT,
            Manifest.permission.REQUESTED_PERMISSION
            ) == PackageManager.PERMISSION_GRANTED -> {
        // You can use the API that requires the permission.
        performAction(...)
    }
    ActivityCompat.shouldShowRequestPermissionRationale(
            this, Manifest.permission.REQUESTED_PERMISSION) -> {
        // In an educational UI, explain to the user why your app requires this
        // permission for a specific feature to behave as expected, and what
        // features are disabled if it's declined. In this UI, include a
        // "cancel" or "no thanks" button that lets the user continue
        // using your app without granting the permission.
        showInContextUI(...)
    }
    else -> {
        // You can directly ask for the permission.
        requestPermissions(CONTEXT,
                arrayOf(Manifest.permission.REQUESTED_PERMISSION),
                REQUEST_CODE)
    }
}

Java

if (ContextCompat.checkSelfPermission(
        CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
        PackageManager.PERMISSION_GRANTED) {
    // You can use the API that requires the permission.
    performAction(...);
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
        this, Manifest.permission.REQUESTED_PERMISSION)) {
    // In an educational UI, explain to the user why your app requires this
    // permission for a specific feature to behave as expected, and what
    // features are disabled if it's declined. In this UI, include a
    // "cancel" or "no thanks" button that lets the user continue
    // using your app without granting the permission.
    showInContextUI(...);
} else {
    // You can directly ask for the permission.
    requestPermissions(CONTEXT,
            new String[] { Manifest.permission.REQUESTED_PERMISSION },
            REQUEST_CODE);
}

אחרי שהמשתמש מגיב לתיבת הדו-שיח של הרשאות המערכת, המערכת מפעילה את ההטמעה של onRequestPermissionsResult() באפליקציה. המערכת מעבירה את תשובת המשתמש לתיבת הדו-שיח של ההרשאה, וגם את קוד הבקשה שהגדרתם, כמו שמוצג בקטע הקוד הבא:

Kotlin

override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {
        PERMISSION_REQUEST_CODE -> {
            // If request is cancelled, the result arrays are empty.
            if ((grantResults.isNotEmpty() &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                // Permission is granted. Continue the action or workflow
                // in your app.
            } else {
                // Explain to the user that the feature is unavailable because
                // the feature requires a permission that the user has denied.
                // At the same time, respect the user's decision. Don't link to
                // system settings in an effort to convince the user to change
                // their decision.
            }
            return
        }

        // Add other 'when' lines to check for other
        // permissions this app might request.
        else -> {
            // Ignore all other requests.
        }
    }
}

Java

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
        int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_CODE:
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission is granted. Continue the action or workflow
                // in your app.
            }  else {
                // Explain to the user that the feature is unavailable because
                // the feature requires a permission that the user has denied.
                // At the same time, respect the user's decision. Don't link to
                // system settings in an effort to convince the user to change
                // their decision.
            }
            return;
        }
        // Other 'case' lines to check for other
        // permissions this app might request.
    }
}

שליחת בקשה להרשאות מיקום

כשמבקשים הרשאות מיקום, צריך לפעול לפי אותן שיטות מומלצות כמו כשמבקשים כל הרשאה אחרת בתחילת ההפעלה. הבדל חשוב אחד שקשור להרשאות מיקום הוא שהמערכת כוללת כמה הרשאות שקשורות למיקום. ההרשאות שאתם מבקשים והאופן שבו אתם מבקשים אותן תלויים בדרישות המיקום של תרחיש השימוש באפליקציה.

מיקום במצב פעיל

אם האפליקציה מכילה תכונה שמשתפת או מקבלת פרטי מיקום רק פעם אחת, או למשך פרק זמן מוגדר, התכונה הזו דורשת גישה למיקום בחזית האפליקציה. דוגמאות:

  • באפליקציית ניווט, תכונה מאפשרת למשתמשים לקבל מסלול מפורט.
  • באפליקציית הודעות, תכונה מאפשרת למשתמשים לשתף את המיקום הנוכחי שלהם עם משתמש אחר.

המערכת מחשיבה שאפליקציה משתמשת במיקום בפורגראונד אם תכונה של האפליקציה ניגשת למיקום הנוכחי של המכשיר באחת מהסיטואציות הבאות:

  • פעילות ששייכת לאפליקציה שלכם גלויה.
  • האפליקציה שלך מפעילה שירות שפועל בחזית. כששירות שפועל בחזית פועל, המערכת מציגה התראה קבועה כדי שהמשתמשים יהיו מודעים לכך. האפליקציה שומרת על הגישה כשהיא פועלת ברקע, למשל כשהמשתמש לוחץ על הלחצן דף הבית במכשיר או מכבה את המסך של המכשיר.

    ב-Android 10 (רמת API‏ 29) ומעלה, צריך להצהיר על סוג של שירות שפועל בחזית מסוג location, כמו שמוצג בקטע הקוד הבא. בגרסאות קודמות של Android, מומלץ להצהיר על הסוג הזה של שירות שפועל בחזית.

    <!-- Recommended for Android 9 (API level 28) and lower. -->
    <!-- Required for Android 10 (API level 29) and higher. -->
    <service
        android:name="MyNavigationService"
        android:foregroundServiceType="location" ... >
        <!-- Any inner elements go here. -->
    </service>

מצהירים על הצורך במיקום במצב פעיל כשהאפליקציה מבקשת את ההרשאה ACCESS_COARSE_LOCATION או את ההרשאה ACCESS_FINE_LOCATION, כמו שמוצג בקטע הקוד הבא:

<manifest ... >
  <!-- Include this permission any time your app needs location information. -->
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

  <!-- Include only if your app benefits from precise location access. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

מיקום ברקע

אפליקציה דורשת גישה למיקום ברקע אם תכונה באפליקציה משתפת מיקום באופן קבוע עם משתמשים אחרים או משתמשת ב-Geofencing API. הנה כמה דוגמאות:

  • באפליקציה לשיתוף מיקום משפחתי, יש תכונה שמאפשרת למשתמשים לשתף את המיקום שלהם עם בני המשפחה באופן רציף.
  • באפליקציית IoT, יש תכונה שמאפשרת למשתמשים להגדיר את המכשירים הביתיים כך שהם יכובו כשהמשתמשים יוצאים מהבית וידלקו מחדש כשהם חוזרים הביתה.

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

ב-Android מגרסה 10 (רמת API 29) ואילך, אתם צריכים להצהיר על ההרשאה ACCESS_BACKGROUND_LOCATION במניפסט של האפליקציה כדי לבקש גישה למיקום ברקע בזמן הריצה. בגרסאות קודמות של Android, כשהאפליקציה מקבלת גישה למיקום בחזית, היא מקבלת באופן אוטומטי גם גישה למיקום ברקע.

<manifest ... >
  <!-- Required only when requesting background location access on
       Android 10 (API level 29) and higher. -->
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

טיפול בדחיית הרשאה

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

  • להפנות את תשומת הלב של המשתמש. צריך להדגיש חלק ספציפי בממשק המשתמש של האפליקציה שבו הפונקציונליות מוגבלת כי לאפליקציה אין את ההרשאה הנדרשת. דוגמאות לפעולות שאפשר לבצע:

    • הצגת הודעה במקום שבו היו אמורים להופיע התוצאות או הנתונים של התכונה.
    • להציג לחצן אחר שמכיל סמל שגיאה וצבע.
  • חשוב להיות ספציפיים. לא להציג הודעה גנרית. במקום זאת, צריך להבהיר אילו תכונות לא זמינות כי לאפליקציה אין את ההרשאה הנדרשת.

  • אל תחסמו את ממשק המשתמש. במילים אחרות, אל תציגו הודעת אזהרה במסך מלא שמונעת מהמשתמשים להמשיך להשתמש באפליקציה שלכם.

במקביל, האפליקציה צריכה לכבד את ההחלטה של המשתמש לדחות בקשת הרשאה. החל מ-Android 11 (רמת API 30), אם המשתמש מקיש על דחייה להרשאה ספציפית יותר מפעם אחת במהלך כל משך החיים של האפליקציה לאחר התקנה על מכשיר מסוים, המשתמש לא רואה את תיבת הדו-שיח של הרשאות המערכת אם האפליקציה מבקשת את ההרשאה הזו שוב. הפעולה של המשתמש מרמזת על 'אל תשאלו אותי שוב'. בגרסאות קודמות, תיבת הדו-שיח של הרשאות המערכת הוצגה למשתמשים בכל פעם שהאפליקציה ביקשה הרשאה, אלא אם הם סימנו בעבר את התיבה או האפשרות "אל תשאל/י שוב".

אם משתמש דוחה בקשת הרשאה יותר מפעם אחת, הדחייה נחשבת לדחייה קבועה. חשוב מאוד להציג למשתמשים בקשות להרשאות רק כשהם צריכים גישה לתכונה ספציפית. אחרת, יכול להיות שתאבדו בטעות את היכולת לבקש שוב הרשאות.

במצבים מסוימים, יכול להיות שההרשאה תידחה באופן אוטומטי, בלי שהמשתמש יבצע פעולה כלשהי. (יכול להיות שהרשאה תוענק באופן אוטומטי). חשוב לא להניח הנחות לגבי התנהגות אוטומטית. בכל פעם שהאפליקציה צריכה לגשת לפונקציונליות שנדרשת לה הרשאה, צריך לבדוק שההרשאה הזו עדיין ניתנת לאפליקציה.

כדי לספק את חוויית המשתמש הכי טובה כשמבקשים הרשאות לאפליקציה, כדאי לעיין גם בשיטות מומלצות לבקשת הרשאות לאפליקציה.

בדיקת סטטוס הדחייה במהלך בדיקה וניפוי באגים

כדי לזהות אם הרשאות נדחו באופן סופי באפליקציה (למטרות ניפוי באגים ובדיקה), משתמשים בפקודה הבאה:

adb shell dumpsys package PACKAGE_NAME

כאשר PACKAGE_NAME הוא שם החבילה שרוצים לבדוק.

הפלט של הפקודה מכיל קטעים שנראים כך:

...
runtime permissions:
  android.permission.POST_NOTIFICATIONS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
  android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_SET|USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
  android.permission.BLUETOOTH_CONNECT: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
...

הרשאות שהמשתמש דחה פעם אחת מסומנות על ידי USER_SET. אם בוחרים באפשרות דחייה פעמיים, ההרשאות נדחות באופן סופי ומסומנות על ידי USER_FIXED.

כדי לוודא שהבודקים רואים את תיבת הדו-שיח של הבקשה במהלך הבדיקה, צריך לאפס את הדגלים האלה אחרי שמסיימים את ניפוי הבאגים באפליקציה. כדי לעשות את זה, משתמשים בפקודה:

adb shell pm clear-permission-flags PACKAGE_NAME PERMISSION_NAME user-set user-fixed

PERMISSION_NAME הוא שם ההרשאה שרוצים לאפס.

PERMISSION_NAME הוא שם ההרשאה שרוצים לאפס.

כדי לראות רשימה מלאה של ההרשאות לאפליקציות ל-Android, אפשר לעבור אל דף ההפניה של ה-API להרשאות.

הרשאות חד-פעמיות

האפשרות &#39;רק הפעם&#39; היא השנייה מבין שלושת הלחצנים בתיבת הדו-שיח.
איור 2. תיבת דו-שיח של המערכת שמופיעה כשאפליקציה מבקשת הרשאה חד-פעמית.

החל מ-Android 11 (רמת API 30), בכל פעם שהאפליקציה מבקשת הרשאה שקשורה למיקום, למיקרופון או למצלמה, תיבת הדו-שיח של ההרשאות שמוצגת למשתמש מכילה אפשרות שנקראת רק הפעם, כמו שמוצג באיור 2. אם המשתמש בוחר באפשרות הזו בתיבת הדו-שיח, האפליקציה מקבלת הרשאה חד-פעמית זמנית.

לאחר מכן, האפליקציה יכולה לגשת לנתונים הרלוונטיים למשך תקופה שתלויה בהתנהגות האפליקציה ובפעולות של המשתמש:

  • בזמן שהפעילות באפליקציה גלויה, האפליקציה יכולה לגשת לנתונים.
  • אם המשתמש מעביר את האפליקציה לרקע, היא יכולה להמשיך לגשת לנתונים לפרק זמן קצר.
  • אם מפעילים שירות שפועל בחזית בזמן שהפעילות גלויה, ואז המשתמש מעביר את האפליקציה לרקע, האפליקציה יכולה להמשיך לגשת לנתונים עד שהשירות שפועל בחזית מפסיק.

תהליך האפליקציה מסתיים כשמבטלים את ההרשאה

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

בפעם הבאה שהמשתמש יפתח את האפליקציה שלכם, ותכונה באפליקציה תבקש גישה למיקום, למיקרופון או למצלמה, תוצג למשתמש שוב בקשה להרשאה.

איפוס הרשאות שלא נעשה בהן שימוש

ב-Android יש כמה דרכים לאפס הרשאות זמן ריצה שלא נעשה בהן שימוש למצב ברירת המחדל שלהן, כלומר למצב 'נדחתה':

הסרת הגישה לאפליקציה

ב-Android 13 (רמת API 33) ומעלה, אפשר להסיר את הגישה של האפליקציה להרשאות זמן ריצה שהאפליקציה כבר לא צריכה. כשמעדכנים את האפליקציה, חשוב לבצע את השלב הזה כדי שהמשתמשים יבינו למה האפליקציה ממשיכה לבקש הרשאות ספציפיות. הידע הזה עוזר לבנות אמון בקרב המשתמשים באפליקציה.

כדי להסיר גישה להרשאת זמן ריצה, מעבירים את שם ההרשאה אל revokeSelfPermissionOnKill(). כדי להסיר גישה לקבוצה של הרשאות בזמן ריצה בבת אחת, מעבירים אוסף של שמות הרשאות אל revokeSelfPermissionsOnKill(). תהליך הסרת ההרשאה מתבצע באופן אסינכרוני, ומפסיק את כל התהליכים שמשויכים למזהה המשתמש של האפליקציה.

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

כדי להודיע למשתמש שהאפליקציה שלך כבר לא דורשת גישה להרשאות מסוימות בתחילת ההפעלה, צריך להציג תיבת דו-שיח בפעם הבאה שהמשתמש יפעיל את האפליקציה. תיבת הדו-שיח יכולה לכלול את רשימת ההרשאות.

איפוס אוטומטי של הרשאות שניתנו לאפליקציות שלא היו בשימוש כבר הרבה זמן

אם האפליקציה שלכם מיועדת ל-Android מגרסה 11 (רמת API‏ 30) ואילך, ולא נעשה בה שימוש במשך כמה חודשים, המערכת מגנה על נתוני המשתמשים על ידי איפוס אוטומטי של הרשאות זמן הריצה הרגישות שהמשתמש העניק לאפליקציה. מידע נוסף זמין במדריך בנושא העברת אפליקציות למצב שינה.

אם צריך, שולחים בקשה להפוך ל-handler שמוגדר כברירת מחדל

יש אפליקציות שזקוקות לגישה למידע רגיש של משתמשים שקשור ליומני שיחות ולהודעות SMS. אם רוצים לבקש את ההרשאות שספציפיות ליומני שיחות ולהודעות SMS ולפרסם את האפליקציה בחנות Play, צריך להציג למשתמש בקשה להגדיר את האפליקציה כhandler שמוגדר כברירת מחדל לפונקציית מערכת מרכזית לפני שמבקשים את ההרשאות האלה בזמן הריצה.

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

מתן כל ההרשאות בזמן הריצה למטרות בדיקה

כדי להעניק באופן אוטומטי את כל ההרשאות בזמן הריצה כשמתקינים אפליקציה באמולטור או במכשיר בדיקה, משתמשים באפשרות -g של הפקודה adb shell install, כמו שמוצג בקטע הקוד הבא:

adb shell install -g PATH_TO_APK_FILE

מקורות מידע נוספים

מידע נוסף על הרשאות זמין במאמרים הבאים:

דוגמאות להרשאות

אפשר גם להשלים את ה-codelab הזה שמציג שיטות מומלצות לשמירה על פרטיות.

‫[12]: /reference/androidx/core/content/ContextCompat#checkSelfPermission(android.content.Context, java.lang.String)

‫[15]: /reference/androidx/core/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int) [16]: /reference/androidx/core/app/ActivityCompat#shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String)