Статьи

Запрос разрешений во время выполнения в Android M и N

Начиная с Android Marshmallow (API 23), пользователи будут запрашивать разрешения во время работы приложения. Таким образом, пользователь может выбирать, какие разрешения он должен предоставлять, не влияя на поток приложения. В этом уроке я расскажу о том, как запрашивать разрешения времени выполнения в Android M и N, как выполнить запрос, получить его результат и затем обработать его.

Запрос разрешения

В Android существует множество user-permissions но я остановлюсь только на некоторых наиболее используемых.

Вы можете найти код для этого проекта в github .

Для начала создайте новый проект в Android Studio, выбрав минимальный уровень API 23 и добавив пустое действие . Это будет единственное мероприятие в проекте.

Объявление разрешений

Откройте AndroidManifest.xml и добавьте следующие разрешения:

 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.CALL_PHONE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/> 

Указанные выше разрешения являются одними из наиболее часто используемых в приложениях для Android, но все они работают аналогичным образом.

С просьбой разрешить

Каждое из этих разрешений будет запрашиваться с помощью кнопки. Обновите код в файле activity_main.xml :

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://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" tools:context="com.example.theodhor.runtimepermissions.MainActivity"> <LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Location" android:id="@+id/location" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button style="?android:attr/buttonStyleSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Write ExStorage" android:id="@+id/write" android:onClick="ask" android:layout_marginBottom="10dp" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Read ExStorage" android:id="@+id/read" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Call" android:id="@+id/call" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Camera" android:id="@+id/camera" android:layout_marginBottom="10dp" android:onClick="ask" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Get Accounts" android:id="@+id/accounts" android:layout_marginBottom="10dp" android:onClick="ask" /> </LinearLayout> </RelativeLayout> 

Это макет, который вы только что создали:

раскладка

На этом этапе, если пользователь установил приложение, ему потребуются следующие разрешения:

Требуются разрешения

Внутри MainActivity.java , после onCreate добавьте этот метод:

 private void askForPermission(String permission, Integer requestCode) { if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission)) { //This is called if user has denied the permission before //In this case I am just asking the permission again ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode); } else { ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode); } } else { Toast.makeText(this, "" + permission + " is already granted.", Toast.LENGTH_SHORT).show(); } } 

Этот метод запрашивает у пользователя разрешения. Сначала проверяется, предоставлено ли запрашиваемое вами разрешение или нет. Если это так, то приложение показывает тост, говорящий о том, что разрешение уже предоставлено. Если разрешение не предоставлено, он проверяет, отказал ли пользователь в этом разрешении ранее. Если разрешение важно для приложения, его следует запросить снова. Если разрешение не было отказано ранее, выполните запрос, вызвав ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);

Каждый запрос на разрешение требует три параметра. Первый — это context , второй — String array разрешений (ей) String array , а третий — requestCode типа Integer . Последний параметр — это произвольный код, прикрепленный к запросу, и может быть любым числом, подходящим для вашего варианта использования. Когда результат возвращается в действие, он содержит этот код и использует его для различения нескольких результатов друг от друга.

Метод готов выполнять запросы, и теперь он должен быть связан с соответствующими кнопками. Каждая из созданных кнопок имеет свойство android:onClick="ask" .

Вам нужно создать public метод ask который будет вызываться при каждом нажатии кнопок. Это код:

 public void ask(View v){ switch (v.getId()){ case R.id.location: askForPermission(Manifest.permission.ACCESS_FINE_LOCATION,LOCATION); break; case R.id.call: askForPermission(Manifest.permission.CALL_PHONE,CALL); break; case R.id.write: askForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,WRITE_EXST); break; case R.id.read: askForPermission(Manifest.permission.READ_EXTERNAL_STORAGE,READ_EXST); break; case R.id.camera: askForPermission(Manifest.permission.CAMERA,CAMERA); break; case R.id.accounts: askForPermission(Manifest.permission.GET_ACCOUNTS,ACCOUNTS); break; default: break; } } 

Вторым параметром метода askForPermission являются случайные целые числа. Добавьте их объявления перед onCreate() :

 static final Integer LOCATION = 0x1; static final Integer CALL = 0x2; static final Integer WRITE_EXST = 0x3; static final Integer READ_EXST = 0x4; static final Integer CAMERA = 0x5; static final Integer ACCOUNTS = 0x6; static final Integer GPS_SETTINGS = 0x7; 

Теперь приложение может выполнять запросы. Следующим шагом является обработка результатов запроса.

Обработка результатов

Для обработки результатов запроса на разрешение onRequestPermissionsResult метод onRequestPermissionsResult . Это код ниже:

 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED){ switch (requestCode) { //Location case 1: askForGPS(); break; //Call case 2: Intent callIntent = new Intent(Intent.ACTION_CALL); callIntent.setData(Uri.parse("tel:" + "{This is a telephone number}")); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { startActivity(callIntent); } break; //Write external Storage case 3: break; //Read External Storage case 4: Intent imageIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(imageIntent, 11); break; //Camera case 5: Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { startActivityForResult(takePictureIntent, 12); } break; //Accounts case 6: AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE); Account[] list = manager.getAccounts(); Toast.makeText(this,""+list[0].name,Toast.LENGTH_SHORT).show(); for(int i=0; i<list.length;i++){ Log.e("Account "+i,""+list[i].name); } } Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show(); } } 

Когда результат возвращается, он проверяет, было ли предоставлено разрешение или нет. Если это так, то requestCode передается в оператор switch для дифференциации результатов. Я добавил дополнительные строки внутри каждого case чтобы показать дальнейшие шаги, но они всего лишь примеры.

Запрос местоположения

В case 1: есть функция с именем askForGPS() . Эта функция предлагает пользователю включить GPS, если он не включен. Если устройство не подключено к GPS после того, как пользователь предоставил разрешение на местоположение, появится диалоговое окно ниже:

Нет диалога GPS

Чтобы показать это диалоговое окно, вам нужен GoogleApiClient . Во-первых, объявите некоторые переменные, добавив эти строки перед onCreate :

 GoogleApiClient client; LocationRequest mLocationRequest; PendingResult<LocationSettingsResult> result; 

И создайте client внутри метода onCreate :

 client = new GoogleApiClient.Builder(this) .addApi(AppIndex.API) .addApi(LocationServices.API) .build(); 

Теперь клиент собран, ему нужно подключиться при запуске приложения и отключиться при его остановке. Добавьте эти два overriden метода в MainActivity :

 @Override public void onStart() { super.onStart(); client.connect(); } @Override public void onStop() { super.onStop(); client.disconnect(); } 

Наконец, добавьте функцию, которая показывает диалог GPS:

 private void askForGPS(){ mLocationRequest = LocationRequest.create(); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mLocationRequest.setInterval(30 * 1000); mLocationRequest.setFastestInterval(5 * 1000); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest); builder.setAlwaysShow(true); result = LocationServices.SettingsApi.checkLocationSettings(client, builder.build()); result.setResultCallback(new ResultCallback<LocationSettingsResult>() { @Override public void onResult(LocationSettingsResult result) { final Status status = result.getStatus(); switch (status.getStatusCode()) { case LocationSettingsStatusCodes.SUCCESS: break; case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: try { status.startResolutionForResult(MainActivity.this, GPS_SETTINGS); } catch (IntentSender.SendIntentException e) { } break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: break; } } }); } 

Вывод

В этом руководстве я показал, как запрашивать разрешения и обрабатывать результаты с помощью новой модели разрешений Android. Цель этой новой модели — дать пользователям возможность контролировать то, что должны делать наши приложения. Я хотел бы услышать ваши комментарии и мнения об этом изменении и о том, как справиться с этим ниже.