Melcocha de Android 6.0. No se puede escribir en la tarjeta SD

Tengo una aplicación que utiliza almacenamiento externo para almacenar fotografías. Según se requiera, en su manifiesto, se solicitan los siguientes permisos

<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

Y utiliza lo siguiente para recuperar el directorio requerido

 File sdDir = Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd", Locale.US); String date = dateFormat.format(new Date()); storageDir = new File(sdDir, getResources().getString( R.string.storagedir) + "-" + date); // Create directory, error handling if (!storageDir.exists() && !storageDir.mkdirs()) { ... fails here 

La aplicación funciona bien en Android 5.1 a 2.3; Ha estado en Google Play durante más de un año.

Tras una actualización de uno de mis teléfonos de prueba (Android One) a 6, ahora está devolviendo un error al intentar crear el directorio necesario, "/ sdcard / Pictures / myapp-yy-mm".

La tarjeta sd está configurada como "Almacenamiento portátil". He formateado la tarjeta sd. Lo he reemplazado. He reiniciado. Todo fue en vano.

Además, la funcionalidad de captura de pantalla integrada de Android (a través de Power + Lower volume) está fallando "debido al espacio de almacenamiento limitado, o no está permitido por la aplicación o su organización".

¿Algunas ideas?

6 Solutions collect form web for “Melcocha de Android 6.0. No se puede escribir en la tarjeta SD”

Me enfrenté al mismo problema. Hay dos tipos de permisos en Android:

  • Peligroso (acceso a contactos, escritura en almacenamiento externo …)
  • Normal

Los permisos normales son aprobados automáticamente por Android mientras que los permisos peligrosos deben ser aprobados por los usuarios de Android.

Esta es la estrategia para obtener permisos peligrosos en Android 6.0

  1. Compruebe si tiene el permiso concedido
  2. Si ya se ha concedido el permiso a su aplicación, continúe y realice normalmente.
  3. Si su aplicación aún no tiene el permiso, pida al usuario que apruebe
  4. Escucha la aprobación del usuario en onRequestPermissionsResult

Aquí está mi caso: Tengo que escribir en el almacenamiento externo.

En primer lugar, compruebo si tengo el permiso:

 ... private static final int REQUEST_WRITE_STORAGE = 112; ... boolean hasPermission = (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED); if (!hasPermission) { ActivityCompat.requestPermissions(parentActivity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE); } 

A continuación, compruebe la aprobación del usuario:

 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_WRITE_STORAGE: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //reload my activity with permission granted or use the features what required the permission } else { Toast.makeText(parentActivity, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show(); } } } } 

Puedes leer más sobre el nuevo modelo de permiso aquí: https://developer.android.com/training/permissions/requesting.html

Primero te daré una lista de permisos peligrosos en Android M y versión posterior

Introduzca aquí la descripción de la imagen Introduzca aquí la descripción de la imagen

A continuación, dé un ejemplo de cómo solicitar permiso en Android M y una versión posterior .

Le pido permiso al usuario WRITE_EXTERNAL_STORAGE .

Primero agrega permiso en tu archivo de menifest android

Paso 1 Declare requestcode

  private static String TAG = "PermissionDemo"; private static final int REQUEST_WRITE_STORAGE = 112; 

Paso 2 Agregue este código cuando desee pedir permiso al usuario

  //ask for the permission in android M int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "Permission to record denied"); if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Permission to access the SD-CARD is required for this app to Download PDF.") .setTitle("Permission required"); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Log.i(TAG, "Clicked"); makeRequest(); } }); AlertDialog dialog = builder.create(); dialog.show(); } else { makeRequest(); } } protected void makeRequest() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE); } 

Paso 3 Agregue el método de sustitución para la solicitud

  @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case REQUEST_WRITE_STORAGE: { if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "Permission has been denied by user"); } else { Log.i(TAG, "Permission has been granted by user"); } return; } } } 

Nota: No se olvide de agregar permiso en el archivo menifest

MEJOR EJEMPLO ABAJO CON PERMISO MÚLTIPLE Y CUBRE TODO EL ESCENARIO

He añadido comentarios para que pueda entender fácilmente.

 import android.Manifest; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; import android.support.annotation.NonNull; 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.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.production.hometech.busycoder.R; import java.util.ArrayList; public class PermissionInActivity extends AppCompatActivity implements View.OnClickListener { private static final int REQUEST_PERMISSION_SETTING = 99; private Button bt_camera; private static final String[] PARAMS_TAKE_PHOTO = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static final int RESULT_PARAMS_TAKE_PHOTO = 11; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_permission_in); bt_camera = (Button) findViewById(R.id.bt_camera); bt_camera.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.bt_camera: takePhoto(); break; } } /** * shouldShowRequestPermissionRationale() = This will return true if the user had previously declined to grant you permission * NOTE : that ActivityCompat also has a backwards-compatible implementation of * shouldShowRequestPermissionRationale(), so you can avoid your own API level * checks. * <p> * shouldShowRequestPermissionRationale() = returns false if the user declined the permission and checked the checkbox to ask you to stop pestering the * user. * <p> * requestPermissions() = request for the permisssiion */ private void takePhoto() { if (canTakePhoto()) { Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show(); } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { Toast.makeText(this, "You should give permission", Toast.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO); } else { ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO); } } // This method return permission denied String[] so we can request again private String[] netPermisssion(String[] wantedPermissions) { ArrayList<String> result = new ArrayList<>(); for (String permission : wantedPermissions) { if (!hasPermission(permission)) { result.add(permission); } } return (result.toArray(new String[result.size()])); } private boolean canTakePhoto() { return (hasPermission(Manifest.permission.CAMERA) && hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)); } /** * checkSelfPermission() = you can check if you have been granted a runtime permission or not * ex = ContextCompat.checkSelfPermission(this,permissionString)== PackageManager.PERMISSION_GRANTED * <p> * ContextCompat offers a backwards-compatible implementation of checkSelfPermission(), ActivityCompat offers a backwards-compatible * implementation of requestPermissions() that you can use. * * @param permissionString * @return */ private boolean hasPermission(String permissionString) { return (ContextCompat.checkSelfPermission(this, permissionString) == PackageManager.PERMISSION_GRANTED); } /** * requestPermissions() action goes to onRequestPermissionsResult() whether user can GARNT or DENIED those permisssions * * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == RESULT_PARAMS_TAKE_PHOTO) { if (canTakePhoto()) { Toast.makeText(this, "You can take picture", Toast.LENGTH_SHORT).show(); } else if (!(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) { final AlertDialog.Builder settingDialog = new AlertDialog.Builder(PermissionInActivity.this); settingDialog.setTitle("Permissioin"); settingDialog.setMessage("Now you need to enable permisssion from the setting because without permission this app won't run properly \n\n goto -> setting -> appInfo"); settingDialog.setCancelable(false); settingDialog.setPositiveButton("Setting", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.cancel(); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, REQUEST_PERMISSION_SETTING); Toast.makeText(getBaseContext(), "Go to Permissions to Grant all permission ENABLE", Toast.LENGTH_LONG).show(); } }); settingDialog.show(); Toast.makeText(this, "You need to grant permission from setting", Toast.LENGTH_SHORT).show(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_PERMISSION_SETTING) { if (canTakePhoto()) { Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show(); } } } } 

Caso especial para el cambio de configuración

Es posible que el usuario gire el dispositivo o de lo contrario activará un cambio de configuración mientras nuestro diálogo de permisos esté en primer plano. Dado que nuestra actividad sigue siendo visible detrás de ese diálogo, nos destruimos y recreamos … pero no queremos volver a levantar el diálogo de permisos de nuevo.

Es por eso que tenemos un booleano, llamado isInPermission, que rastrea si estamos o no en medio de solicitar permisos. Nos mantenemos en ese valor en onSaveInstanceState() :

 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(STATE_IN_PERMISSION, isInPermission); } 

Lo restauramos en onCreate() . Si no tenemos todos los permisos deseados, pero isInPermission es verdad, omitimos solicitar los permisos, ya que estamos en medio de hacerlo ya.

Android cambió la forma en que funcionan los permisos con Android 6.0, que es la razón de sus errores. Usted tiene que realmente solicitar y comprobar si el permiso fue concedido por el usuario para su uso. Así que los permisos en el archivo de manifiesto sólo funcionarán para api debajo de 21. Compruebe este enlace para ver un fragmento de cómo se solicitan los permisos en api23 http://android-developers.blogspot.nl/2015/09/google-play-services-81- And-android-60.html? M = 1

Código:-

 If (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_RC); return; }` ` @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == STORAGE_PERMISSION_RC) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { //permission granted start reading } else { Toast.makeText(this, "No permission to read external storage.", Toast.LENGTH_SHORT).show(); } } } } 

Tal vez usted no puede utilizar la clase manifesto de código generado en su proyecto. Por lo tanto, puede utilizar la clase manifest de android sdk "android.Manifest.permission.WRITE_EXTERNAL_STORAGE". Pero en la versión de Marsmallow tienen 2 permisos que deben conceder es WRITE y READ EXTERNAL STORAGE en la categoría de almacenamiento. Vea mi programa, mi programa solicitará permiso hasta que el usuario elija sí y haga algo después de que los permisos se concedan.

  if (Build.VERSION.SDK_INT >= 23) { if (ContextCompat.checkSelfPermission(LoginActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(LoginActivity.this, android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(LoginActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1); } else { //do something } } else { //do something } 

Derecha. Así que finalmente llegué a la parte inferior del problema: se trataba de una actualización OTA en mal estado.

Mis sospechas se intensificaron después de mi Garmin Fenix ​​2 no fue capaz de conectarse a través de bluetooth y después de google "Marshmallow problemas de actualización". De todos modos, un "restablecimiento de fábrica" ​​arregló el problema.

Sorprendentemente, el reinicio no devolvió el teléfono a la Kitkat original; En su lugar, el proceso de borrado recogió el paquete de actualización de OTA descargado 6.0 y corrió con él, resultando (supongo) en una actualización "más limpia".

Por supuesto, esto significaba que el teléfono perdió todas las aplicaciones que había instalado. Pero, las aplicaciones recién instaladas, incluyendo la mía, funcionan sin cambios (es decir, hay compatibilidad hacia atrás). ¡Uf!

La documentación de Android en los estados Manifest.permission.Manifest.permission.WRITE_EXTERNAL_STORAGE:

A partir del nivel 19 de la API, este permiso no es necesario para leer / escribir archivos en los directorios específicos de la aplicación devueltos por getExternalFilesDir (String) y getExternalCacheDir ().


Creo que esto significa que no tiene que codificar para la implementación en tiempo de ejecución del permiso WRITE_EXTERNAL_STORAGE a menos que la aplicación esté escribiendo en un directorio que no sea específico para su aplicación.

Puede definir la versión max sdk en el manifiesto por permiso como:

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="19" /> 

También asegúrese de cambiar el SDK de destino en build.graddle y no en el manifiesto, la configuración gradle siempre sobrescribirá la configuración del manifiesto.

 android { compileSdkVersion 23 buildToolsVersion '23.0.1' defaultConfig { minSdkVersion 17 targetSdkVersion 22 } 
  • ¿Cómo eliminar un archivo de la tarjeta SD?
  • Error de escritura de archivo de texto en la tarjeta SD
  • ¿Tengo que declarar WRITE_EXTERNAL_STORAGE y READ_EXTERNAL_STORAGE?
  • Almacenamiento externo de Android entre las marcas para móviles
  • ¿Cómo eliminar una imagen en miniatura mediante programación de galera después de eliminar una imagen?
  • Android - Almacenamiento interno vs almacenamiento externo cuando la aplicación se instala en la tarjeta SD
  • Guardar archivo en android, por lo que el usuario no puede leer
  • ¿Cómo calcula Android el caché y el tamaño de los datos?
  • ¿Cuál es la diferencia entre el acceso al archivo desde la carpeta de activos o la tarjeta SD
  • Tome la copia de seguridad de todos los archivos de instalación de apk en sdcard de forma programada en android
  • Copiar un archivo de texto en la tarjeta SD en el momento de la instalación de la aplicación?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.