ברוך הבא לדוגמא להרשאות ריצת Android. עם הצגת Android 6.0 Marshmallow, שינתה Google את אופן הטיפול בהרשאות על ידי היישום. במדריך זה נסתכל על ההרשאות החדשות של Android בזמן ריצה שהוצגו וכיצד לטפל בהן. אם לא ייטול טוב, זה יכול לגרום לקריסת היישום.
מהן ההרשאות בזמן ריצה של Android?
עם צפייה ראשונית של Android 6.0 (SDK 23), המשתמשים מתבקשים להעניש להרשאות מסוימות בזמן ריצה כאשר הן הופכות חיוניות לשימוש. אז השאלה הראשונית שבאה לנו לראש – האם היישומים הישנים ירוצו על Android Marshmallow? התשובה היא כן אם targetSdkVersion הוא 22 או פחות. לכן ההרשאות בזמן ריצה של Android תומכות באחור. אך זה אינו אומר שאנו יכולים לעבוד עם מודל ההרשאות הישן על ידי הגדרת גרסת ה-SDK ל-22. משתמש שמשתמש ב-Marshmallow יכול לסרב להרשאות מסוכנות (נדבך בהרשאות רגילות ומסוכנות מאוחר יותר) מתוך ההגדרות->יישומים->הרשאות. במקרה שננסה לקרוא לפונקציה שדורשת הרשאה שהמשתמש עדיין לא אישר, הפונקציה תזרוק יוצא מן הכלל (java.lang.SecurityException
) שיביא לקריסת היישום. לכן אנו צריכים ליישם מחדש את מודל ההרשאות החדש של Android ביישומנו.
הרשאות אנדרואיד מסוכנות ורגילות
אנדרואיד מגדירה חלק מההרשאות כמסוכנות וחלקן כרגילות. הדבר המשותף בין שני הסוגים הוא שעליהם להיות מוגדרים בקובץ ה-Manifest. מאנדרואיד 6.0 רק הרשאות מסוכנות מאומתות בזמן ריצה, ולא הרשאות רגילות. דוגמה להרשאה רגילה היא android.permission.INTERNET
. הרשאות מסוכנות מקובצות לקטגוריות שמקלות על המשתמש להבין מה הוא מאשר ליישום לעשות. אם המשתמש מאשר רשות אחת בקבוצה/קטגוריה הוא מאשר את כל הקבוצה/הקטגוריה כולה. דוגמה להרשאה מסוכנת היא android.permission.FINE_LOCATION
ו-android.permission.COARSE_LOCATION
. הפעלת אחת מרשויות המיקום מאפשרת את כולן.
בקשת הרשאות בזמן ריצת Android
השיטה requestPermissions(String[] permissions, int requestCode);
היא שיטה ציבורית המשמשת לבקשת הרשאות מסוכנות. ניתן לבקש מספר הרשאות מסוכנות על ידי העברת מערך מחרוזת של הרשאות. הערה: הרשאות של אנדרואיד ששייכות לשני קבוצות שונות יפעילו חלון דיאלוג נפרד עבור כל אחת מהן. אם הן שייכות לאותה קבוצה, אז יוצג רק חלון דיאלוג אחד. תוצאות הבקשות יועברו לשיטה onRequestPermissionResult
. לדוגמא: נניח שברצוננו לגשת למצלמה ולמיקום באפליקציה שלנו. שתי אלו הן הרשאות מסוכנות. נציג הודעה המבקשת גישה להרשאות אלו כאשר האפליקציה מושקעת. נוסיף את ההרשאות למערך המחרוזת ונקרא ל- requestPermissions כפי שמוצג למטה:
String[] perms = {"android.permission.FINE_LOCATION", "android.permission.CAMERA"};
int permsRequestCode = 200;
requestPermissions(perms, permsRequestCode);
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
switch(permsRequestCode){
case 200:
boolean locationAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;
break;
}
}
עכשיו אנו לא רוצים שהמשתמש ימשיך לקבל הרשאות שכבר אישר. גם אם ההרשאה אושרה מראש, עדיין חשוב לבדוק שוב כדי לוודא שהמשתמש לא ביטל את ההרשאה מאוחר יותר. לשם כך, יש לקרוא לשיטה הבאה על כל הרשאה:
checkSelfPermission(String perm);
השיטה מחזירה ערך שלם של PERMISSION_GRANTED או PERMISSION_DENIED. הערה: אם המשתמש דוחה הרשאה שקריטית באפליקציה, אז משתמשים ב- shouldShowRequestPermissionRationale(String permission);
כדי לתאר למשתמש את צורך ההרשאה. נפתח אפליקציה שבודקת האם ההרשאה כבר קיימת. אם לא, נבקש אותה בזמן ריצה.
מבנה פרויקט להרשאות זמן ריצה של Android
קוד להרשאות זמן ריצה של Android
הקובץ content_main.xml
מכיל שני כפתורים לבדיקה ובקשה של הרשאות.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.journaldev.runtimepermissions.MainActivity"
tools:showIn="@layout/activity_main">
<Button
android:id="@+id/check_permission"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:text="Check Permission"/>
<Button
android:id="@+id/request_permission"
android:layout_below="@+id/check_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Request Permission"/>
</RelativeLayout>
הקובץ MainActivity.java
מוגדר כך למטה.
package com.journaldev.runtimepermissions;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int PERMISSION_REQUEST_CODE = 200;
private View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button check_permission = (Button) findViewById(R.id.check_permission);
Button request_permission = (Button) findViewById(R.id.request_permission);
check_permission.setOnClickListener(this);
request_permission.setOnClickListener(this);
}
@Override
public void onClick(View v) {
view = v;
int id = v.getId();
switch (id) {
case R.id.check_permission:
if (checkPermission()) {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view, "Please request permission.", Snackbar.LENGTH_LONG).show();
}
break;
case R.id.request_permission:
if (!checkPermission()) {
requestPermission();
} else {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
}
break;
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getApplicationContext(), ACCESS_FINE_LOCATION);
int result1 = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0) {
boolean locationAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (locationAccepted && cameraAccepted)
Snackbar.make(view, "Permission Granted, Now you can access location data and camera.", Snackbar.LENGTH_LONG).show();
else {
Snackbar.make(view, "Permission Denied, You cannot access location data and camera.", Snackbar.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
showMessageOKCancel("You need to allow access to both the permissions",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{ACCESS_FINE_LOCATION, CAMERA},
PERMISSION_REQUEST_CODE);
}
}
});
return;
}
}
}
}
break;
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
}
הערה: הוסף את ההרשאות שיש לבדוק בזמן ריצה בקובץ ה-Manifest מעל לתגית היישום כפי שמוצג למטה:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
בקוד לעיל, ההרשאות שנבדקות ומבוקשות הן CAMERA ו-LOCATION. יבוא של שם מלא של מחלקת ההרשאה הסטטית מאפשר לנו לכתוב רק את אובייקט ההרשאה במקום הנתיב המושלם. checkPermission()
קורא ל- checkSelfPermission על כל אחת מההרשאות. requestPermission()
קוראת ל- ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
. onRequestPermissionsResult
בודקת אם ההרשאות אושרו או לא. בקוד שלנו אם שתי ההרשאות לא אושרו, דיאלוג אזהרה מופיע המציין את הצורך החובה לבקש את ההרשאות. כדי לעשות זאת, shouldShowRequestPermissionRationale(String permission)
נקראת, שמפעילה דיאלוג התראה המציין את הצורך בהרשאות. ניתן לבטל את ההרשאות ידנית מההגדרות->יישומים->הרשאות. הערה: שיטות ההרשאה בריצת הזמן זמינות רק מאז API 23. לכן תנאי זה נבדק בכל אחת מהשיטות:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
פלט דוגמא ליישום דוגמת ההרשאות של אנדרואיד בפעולה מוצג למטה. זה מסיים את המדריך הזה. ניתן להוריד את פרויקט ההרשאות של אנדרואיד הסופי מהקישור למטה.
הורד פרויקט דוגמת הרשאות אנדרואיד
אתר: https://developer.android.com/training/permissions/requesting.html
Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example