Android 6.0 Marshmallow. Impossible d'écrire sur la carte SD

J'ai une application qui utilise un stockage externe pour stocker des photos. Au besoin, dans son manifeste, les autorisations suivantes sont demandées

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

Et il utilise ce qui suit pour récupérer le répertoire requis

  • Portant une application de 100 activités à Fragments sans mourir
  • Android: Impossible d'instancier l'application
  • Comment mettre en œuvre Google Maps recherche par adresse dans Android?
  • OnPostExecute n'est pas appelé dans AsyncTask (exception Runtime du gestionnaire)
  • Problème de connexion Bluetooth Android
  • IllegalStateException: <MyFragment> n'est pas actuellement dans le FragmentManager
  •  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 

    L'application fonctionne bien sur Android 5.1 à 2.3; Il est sur Google Play depuis plus d'un an.

    Suite à une mise à niveau de l'un de mes téléphones de test (Android One) à 6, il est maintenant en train de renvoyer une erreur en essayant de créer le répertoire requis, "/ sdcard / Pictures / myapp-yy-mm".

    La carte sd est configurée comme "stockage portable". J'ai formé la carte sd. Je l'ai remplacé. J'ai redémarré. Tout en rien.

    En outre, la fonctionnalité intégrée de capture d'écran Android (via Power + Lower volume) échoue "en raison d'un espace de stockage limité, ou elle n'est pas autorisée par l'application ou votre organisation".

    Des idées?

  • Erreur = Impossible de trouver l'information d'instrumentation pour: ComponentInfo {}
  • Comment "installer" la barre d'action Sherlock?
  • Définir le texte de vue aligné au centre dans spinner dans Android
  • Quelle est la différence entre la barre d'outils AppBarLayout vs?
  • Google Glass: GDK avec Android Studio
  • Comment faire une vue pour dessiner sur une toile?
  • 6 Solutions collect form web for “Android 6.0 Marshmallow. Impossible d'écrire sur la carte SD”

    J'ai eu le même problème. Il existe deux types d'autorisations dans Android:

    • Dangereux (accès aux contacts, écrit sur le stockage externe …)
    • Normal

    Les autorisations normales sont automatiquement approuvées par Android, tandis que les utilisateurs dangereux doivent être approuvés par des autorisations dangereuses.

    Voici la stratégie pour obtenir des autorisations dangereuses dans Android 6.0

    1. Vérifiez si vous avez la permission accordée
    2. Si votre application a déjà obtenu l'autorisation, continuez et effectuez-vous normalement.
    3. Si votre application n'a pas encore la permission, demandez à l'utilisateur d'approuver
    4. Écoutez l'approbation de l'utilisateur dans onRequestPermissionsResult

    Voici mon cas: je dois écrire sur le stockage externe.

    Tout d'abord, je vérifie si j'ai la permission:

     ... 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); } 

    Vérifiez ensuite l'approbation de l'utilisateur:

     @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(); } } } } 

    Vous pouvez en savoir plus sur le nouveau modèle d'autorisation ici: https://developer.android.com/training/permissions/requesting.html

    D'abord, je vais vous donner une liste de permissions dangereuses dans Android M et version ultérieure

    Entrez la description de l'image ici Entrez la description de l'image ici

    Ensuite, vous donner un exemple de la demande d'autorisation dans Android M et une version ultérieure .

    Je demande à l'utilisateur de WRITE_EXTERNAL_STORAGE l' autorisation.

    Permettez-moi d'ajouter d'abord votre fichier menant à l'Android

    Étape 1 Déclarez le code de demande

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

    Étape 2 Ajoutez ce code lorsque vous souhaitez demander à l'utilisateur une autorisation

      //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); } 

    Étape 3 Ajouter une méthode de substitution pour la demande

      @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; } } } 

    Remarque: N'oubliez pas d'ajouter l'autorisation dans le fichier le plus menant

    MEILLEUR EXEMPLE AU-DESSOUS AVEC MULTIPLES PERMISSION PLUS COUVERT TOUT LE SCÉNARIO

    J'ai ajouté des commentaires afin que vous puissiez facilement comprendre.

     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(); } } } } 

    Cas spécial pour changement de configuration

    Il est possible que l'utilisateur tourne l'appareil ou déclenche un changement de configuration alors que notre dialogue d'autorisation est au premier plan. Puisque notre activité est encore visible derrière ce dialogue, nous sommes détruits et recréés … mais nous ne voulons pas relancer la boîte de dialogue d'autorisation.

    C'est pourquoi nous avons un boolean, nommé isInPermission, qui suit si nous sommes ou non au milieu des autorisations requises. Nous onSaveInstanceState() cette valeur dans onSaveInstanceState() :

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

    Nous le onCreate() dans onCreate() . Si nous ne détenons pas toutes les autorisations souhaitées, mais isInPermission est vrai, nous ignorons la demande des autorisations, car nous sommes en train de le faire déjà.

    Android a changé la façon dont les autorisations fonctionnent avec Android 6.0, c'est la raison de vos erreurs. Vous devez effectivement demander et vérifier si l'autorisation a été accordée par l'utilisateur à utiliser. Donc, les autorisations dans le fichier manifeste ne fonctionneront que pour l'api ci-dessous 21. Vérifiez ce lien pour un extrait de la façon dont les autorisations sont demandées dans api23 http://android-developers.blogspot.nl/2015/09/google-play-services-81- And-android-60.html? M = 1

    Code:-

     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(); } } } } 

    Peut-être que vous ne pouvez pas utiliser la classe manifeste à partir du code généré dans votre projet. Donc, vous pouvez utiliser la classe manifeste de android sdk "android.Manifest.permission.WRITE_EXTERNAL_STORAGE". Mais dans la version Marsmallow, deux autorisations doivent être accordées: Ecrivez et LISEZ LE STOCKAGE EXTERNE dans la catégorie de stockage. Consultez mon programme, mon programme demandera l'autorisation jusqu'à ce que l'utilisateur choisisse oui et fasse quelque chose après l'octroi des autorisations.

      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 } 

    Droite. J'ai donc finalement atteint le fond du problème: c'était une mise à niveau OTA malicieuse sur place.

    Mes soupçons se sont intensifiés après que mon Garmin Fenix ​​2 n'ait pas pu se connecter via Bluetooth et après avoir googlé "problèmes de mise à jour Marshmallow". Quoi qu'il en soit, une "Réinitialisation d'usine" a corrigé le problème.

    Étonnamment, la réinitialisation n'a pas retourné le téléphone à l'original Kitkat; Au lieu de cela, le processus d'essuyage a repris le package de mise à niveau téléchargé OTA 6 et a couru avec lui, résultant (je suppose) dans une mise à niveau "plus propre".

    Bien sûr, cela signifie que le téléphone a perdu toutes les applications que j'avais installées. Mais, les applications fraîchement installées, y compris les miennes, fonctionnent sans modification (c'est-à-dire qu'il existe une compatibilité ascendante). Ouf!

    Documentation Android sur Manifest.permission.Manifest.permission.WRITE_EXTERNAL_STORAGE indique:

    À partir du niveau API 19, cette autorisation n'est pas nécessaire pour lire / écrire des fichiers dans les répertoires spécifiques à votre application retournés par getExternalFilesDir (String) et getExternalCacheDir ().


    Je pense que cela signifie que vous ne devez pas coder pour l'exécution de l'autorisation WRITE_EXTERNAL_STORAGE sauf si l'application écrit sur un répertoire qui n'est pas spécifique à votre application.

    Vous pouvez définir la version max sdk dans le manifeste par autorisation comme:

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

    Assurez-vous également de modifier le SDK cible dans la build.graddle et non le manifeste, les réglages de gradle remplacent toujours les paramètres de manifestation.

     android { compileSdkVersion 23 buildToolsVersion '23.0.1' defaultConfig { minSdkVersion 17 targetSdkVersion 22 } 
    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.