Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Android Permissions — полный гайд
Пермиссии (permissions) в Android — это механизм контроля доступа приложения к чувствительным ресурсам устройства. Это важная часть безопасности и приватности в Android.
Историческая справка
Система пермиссий эволюционировала:
- API 22 и ниже — install-time permissions (разрешение только при установке)
- API 23+ — runtime permissions (разрешение во время работы приложения)
- API 31+ — scoped storage, package visibility
Категории пермиссий
1. Normal Permissions
Низкий риск, автоматически предоставляются при установке:
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
Эти пермиссии не требуют запроса пользователя во время выполнения.
2. Dangerous Permissions (Runtime Permissions)
Высокий риск для приватности, требуют явного согласия пользователя:
<!-- Камера -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- Геолокация -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Контакты -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- Хранилище (до API 30) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Микрофон -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Календарь -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
Permission Groups
Далось permissions сгруппированы в permission groups. Если пользователь дал разрешение на одну, остальные в группе получат его автоматически:
- CALENDAR → READ_CALENDAR, WRITE_CALENDAR
- CAMERA → CAMERA
- CONTACTS → READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS
- LOCATION → ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION
- MICROPHONE → RECORD_AUDIO
- PHONE → READ_PHONE_STATE, READ_PHONE_NUMBERS, CALL_PHONE, USE_SIP
- SENSORS → BODY_SENSORS
- SMS → SEND_SMS, RECEIVE_SMS, READ_SMS, RECEIVE_WAP_PUSH, RECEIVE_MMS
- STORAGE → READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE
Flutter: Запрос пермиссий
В Flutter используется пакет permission_handler:
import 'package:permission_handler/permission_handler.dart';
class PermissionService {
// Запрос одной пермиссии
Future<bool> requestCameraPermission() async {
final status = await Permission.camera.request();
return status.isGranted;
}
// Запрос нескольких пермиссий
Future<Map<Permission, PermissionStatus>> requestMultiple() async {
final statuses = await [
Permission.camera,
Permission.location,
Permission.microphone,
].request();
return statuses;
}
// Проверка текущего статуса
Future<bool> isCameraPermissionGranted() async {
final status = await Permission.camera.status;
return status.isGranted;
}
// Запрос с обработкой всех возможных статусов
Future<void> requestLocationPermission() async {
final status = await Permission.location.request();
if (status.isGranted) {
print('Location permission granted');
} else if (status.isDenied) {
print('Location permission denied');
} else if (status.isPermanentlyDenied) {
print('Location permission permanently denied, open app settings');
openAppSettings();
} else if (status.isRestricted) {
print('Location permission restricted by system');
}
}
// Проверка нескольких пермиссий
Future<bool> hasAllRequiredPermissions() async {
final statuses = await [
Permission.camera,
Permission.microphone,
].status;
return statuses.values.every((status) => status.isGranted);
}
}
Правильный паттерн
class CameraScreen extends StatefulWidget {
@override
State<CameraScreen> createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
late PermissionService _permissionService;
@override
void initState() {
super.initState();
_permissionService = PermissionService();
_checkAndRequestPermissions();
}
Future<void> _checkAndRequestPermissions() async {
final isGranted = await _permissionService.requestCameraPermission();
if (isGranted) {
_initializeCamera();
} else {
_showPermissionDeniedDialog();
}
}
void _initializeCamera() {
// Инициализация камеры
}
void _showPermissionDeniedDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Camera Permission Required'),
content: Text('This app needs camera access to function properly'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
),
TextButton(
onPressed: () {
openAppSettings();
Navigator.pop(context);
},
child: Text('Open Settings'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Camera')),
body: Center(child: Text('Camera Screen')),
);
}
}
Статусы пермиссий
enum PermissionStatus {
granted, // Разрешено
denied, // Отказано (может быть запрошено снова)
permanentlyDenied, // Отказано с опцией "Don't ask again"
restricted, // Ограничено системой (родительский контроль)
limited, // Ограниченный доступ (только iOS 14+)
}
Best Practices
1. Запрашивай пермиссии в нужный момент
// ❌ Плохо: запрос всех пермиссий при запуске
if (mounted) {
await requestAllPermissions();
}
// ✅ Хорошо: запрос при использовании функции
FloatingActionButton(
onPressed: () async {
if (await Permission.camera.request().isGranted) {
openCamera();
}
},
)
2. Обясни зачем нужна пермиссия Показывай UI (rationale) перед запросом:
if (await Permission.location.shouldShowRequestRationale) {
showExplanationDialog();
}
final status = await Permission.location.request();
3. Обрабатывай permanentlyDenied Если пользователь выбрал "Don't ask again", отправь в настройки:
if (status.isPermanentlyDenied) {
openAppSettings();
}
4. Кэшируй результаты
class PermissionCache {
static final Map<Permission, bool> _cache = {};
static Future<bool> hasPermission(Permission permission) async {
if (_cache.containsKey(permission)) {
return _cache[permission]!;
}
final granted = (await permission.status).isGranted;
_cache[permission] = granted;
return granted;
}
}
Заключение
Пермиссии в Android — это фундаментальная часть безопасности. За 10+ лет опыта я вижу, что правильная работа с ними критична для:
- Безопасности приложения и данных пользователя
- Доверия пользователей
- Прохождения App Store/Play Store review
- Стабильности приложения (краши при отсутствии пермиссий)
Всегда запрашивай пермиссии при необходимости, объясняй зачем они нужны и обрабатывай все возможные статусы.