Android Marshmallow: el cambio de permisos en tiempo de ejecución bloquea la aplicación
android-6.0-marshmallow android-permissions (3)
Observé que la aplicación se vuelve a crear, no entiendo por qué sucede esto. ¡Cualquier sugerencia para rectificar este problema sería bienvenida!
Porque cuando los permisos cambian, el "estado" de la aplicación debe ser invalidado. La forma correcta de hacerlo es destruir el contexto raíz, que es la aplicación misma.
Debe verificar el estado de los permisos otorgados cada vez que consulte los métodos API que requieren estos permisos.
No puede confiar en alguna bandera de
SharedPreferences
que indica que "el
usuario otorgó los permisos para la incorporación, ok, diviértase
"
Haga que su aplicación sea apátrida en este sentido.
Por ejemplo, puede crear alguna BaseActivity / BaseFragment o Utility y mover toda la lógica de verificación allí.
Marshmallow ha rediseñado para obtener permisos. Entonces manejó los permisos antes de llamar al método que necesita permisos y funciona bien, pero se bloquea en el siguiente escenario:
Paso 1: abrió la aplicación y otorgó todos los permisos necesarios
Paso 2: haga clic en el botón Inicio (para que la aplicación esté en segundo plano)
Paso 3: cambió manualmente los permisos en la Configuración
Paso 4: Lanzó la aplicación desde multitareas, ahora se bloquea porque el contexto de la aplicación deja de ser válido
Observé que la aplicación se vuelve a crear, no entiendo por qué sucede esto. ¡Cualquier sugerencia para rectificar este problema sería bienvenida!
Definir un valor booleano al principio
private boolean isPermissionGranted = false;
Y luego verifique si se otorgó el permiso:
if (!isPermissionGranted) {
checkPermission();
}
El código real para la verificación de permisos de tiempo de ejecución es el siguiente:
private void checkPermission() {
int hasPermission = ContextCompat.checkSelfPermission(UserProfile.this, Manifest.permission.CAMERA);
int hasWritePermission = ContextCompat.checkSelfPermission(UserProfile.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasPermission != PackageManager.PERMISSION_GRANTED && hasWritePermission != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(UserProfile.this, Manifest.permission.CAMERA) && !ActivityCompat.shouldShowRequestPermissionRationale(UserProfile.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showMessage(getString(R.string.allow_access_to_camera_external_storage),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(UserProfile.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
}
});
return;
}
ActivityCompat.requestPermissions(UserProfile.this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
return;
} else {
isPermissionGranted = true;
}
}
private void showMessage(String message, DialogInterface.OnClickListener listener) {
new AlertDialog.Builder(UserProfile.this)
.setMessage(message)
.setPositiveButton(R.string.ok, listener)
.setNegativeButton(R.string.cancel, null)
.create()
.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_PERMISSIONS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isPermissionGranted = true;
} else {
isPermissionGranted = false;
Toast.makeText(UserProfile.this, R.string.permission_denied, Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Puede tomar la referencia del código anterior e implementarlo en su aplicación.
Se debe a las características adicionales agregadas de Marshmallow. Debe solicitar al usuario en tiempo de ejecución. Para esto usa esta clase que he hecho. Luego úselo cuando sea necesario
public class AppPermission {
public static boolean isMarshmallowPlusDevice() {
return Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1;
}
@TargetApi(Build.VERSION_CODES.M)
public static boolean isPermissionRequestRequired(Activity activity, @NonNull String[] permissions, int requestCode) {
if (isMarshmallowPlusDevice() && permissions.length > 0) {
List<String> newPermissionList = new ArrayList<>();
for (String permission : permissions) {
if (PackageManager.PERMISSION_GRANTED != activity.checkSelfPermission(permission)) {
newPermissionList.add(permission);
}
}
if (newPermissionList.size() > 0) {
activity.requestPermissions(newPermissionList.toArray(new String[newPermissionList.size()]), requestCode);
return true;
}
}
return false;
}
}
Luego ponga este código donde requiera permiso del usuario.
if (!AppPermission.isPermissionRequestRequired(SignInActivity.this, new String[]{"android.permission.GET_ACCOUNTS"},
REQUEST_APP_PERMISSION)) {
// Your code if permission available
}
Después de esto, en su
Fragment
o
Activity
ponga este código:
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_APP_PERMISSION:
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
int grantResult = grantResults[i];
switch (permission) {
case "android.permission.GET_ACCOUNTS":
if (PackageManager.PERMISSION_GRANTED == grantResult) {
// Your code
}
break;
}
}
break;
}
}
El código anterior es para solicitar permiso para
GET_ACCOUNTS
, puede cambiarlo a lo que sea necesario.