Android 6.0 Write to external SD Card

2019-04-15 20:17发布

问题:

I tried everything to write on external SD card on Android 6.0, but I am not able to write on it.

I did research on stackoverflow and found lot of things but none works. Here is my code

String extPath = System.getenv("SECONDARY_STORAGE") + "/Android/data/com.gvm.externalstorage.externalstoragetest/";
File file = new File(extPath,"myFiles");

            if (!file.exists()) {
                boolean dirResult = file.mkdirs();
                Log.e("Directory Exist", dirResult + " Directory created");
            } else {
                Log.e("Directory Exist", "Exist");
                Log.e("Direcotry Path",file.getAbsolutePath());
            }
            //String displayname = fileName.replace("%20", " ");
            File outputFile = new File(file, "mytest5.txt");
            outputFile.createNewFile();

This code works on Android 5.0 but not on Android 6.0.

Then I tried this path as well, and that gives me permission error, I have set all permission and managed code for runtime permission as well.

/mnt/media_rw/6AC9-083B

File write failed: java.io.IOException: open failed: EACCES (Permission denied)

If anyone can help me it would be great as I am trying this since last 3 days.

Thanks, Anvesh

回答1:

After long hard work I figured out a solution. In Android 6.0 it's not going to give you SD Card path always using this:

System.getenv("SECONDARY_STORAGE") 

or this

Environment.getExternalStorageDirectory()

So I retrieved external SD Card path using this

File[] fs = context.getExternalFilesDirs(null);
            String extPath = "";
            // at index 0 you have the internal storage and at index 1 the real external...
            if (fs != null && fs.length >= 2)
            {
                extPath = fs[1].getAbsolutePath();
                Log.e("SD Path",fs[1].getAbsolutePath());
            }

Rest everything will remain same for permission and all.

Thanks to those who helped me.



回答2:

From API 23+(6.0) you need to request the read/write permissions even if they are already in your manifest known as Requesting Permissions at Run Time.

from docs

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. This approach streamlines the app install process, since the user does not need to grant permissions when they install or update the app. It also gives the user more control over the app's functionality; for example, a user could choose to give a camera app access to the camera but not to the device location. The user can revoke the permissions at any time, by going to the app's Settings screen.

java

// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};

/**
 * Checks if the app has permission to write to device storage
 *
 * If the app does not has permission then the user will be prompted to grant permissions
 *
 * @param activity
 */
public static void verifyStoragePermissions(Activity activity) {
    // Check if we have write permission
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        // We don't have permission so prompt the user
        ActivityCompat.requestPermissions(
                activity,
                PERMISSIONS_STORAGE,
                REQUEST_EXTERNAL_STORAGE
        );
    }
}

AndroidManifest.xml

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


回答3:

I think u should check your app permission first, to make sure your storage permission has been turned on.

If there's no storage permission: Please check if u use this permission in your AndroidManifest

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

If the storage permission has been turned off: Please check your runtime permission, maybe u can refer to this code

private void checkPermissions() {
    if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        return;
    }

    final List<String> permissionsList = new ArrayList<String>();
    permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
    permissionsList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
    permissionsList.add(Manifest.permission.WRITE_CALENDAR);
    permissionsList.add(Manifest.permission.READ_PHONE_STATE);

    int permissionCheckLocation = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
    int permissionCheckStorage = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    int permissionCheckCalendar = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.WRITE_CALENDAR);
    int permissionCheckPhoneState = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.READ_PHONE_STATE);

    boolean locationPermission=permissionCheckLocation == PackageManager.PERMISSION_GRANTED?true:false;
    boolean storagePermission=permissionCheckStorage == PackageManager.PERMISSION_GRANTED?true:false;
    boolean calendarPermission=permissionCheckCalendar == PackageManager.PERMISSION_GRANTED?true:false;
    boolean phoneStatePermission=permissionCheckPhoneState == PackageManager.PERMISSION_GRANTED?true:false;


    boolean shouldShowLocationPermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
    boolean shouldShowStoragePermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    boolean shouldShowCalendarPermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.WRITE_CALENDAR);
    boolean shouldShowPhoneStatePermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.READ_PHONE_STATE);

    if (permissionCheckLocation == PackageManager.PERMISSION_GRANTED && permissionCheckStorage == PackageManager.PERMISSION_GRANTED
            && permissionCheckCalendar == PackageManager.PERMISSION_GRANTED && permissionCheckPhoneState == PackageManager.PERMISSION_GRANTED){
        return;
    }else if(((!locationPermission&&!shouldShowLocationPermission)||(!storagePermission&&!shouldShowStoragePermission)
            ||(!calendarPermission&&!shouldShowCalendarPermission)||(!phoneStatePermission&&!shouldShowPhoneStatePermission))&&appContext.localCheckPermission){
        showMessageOKCancel("You need to allow access these permissions",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
                        startActivityForResult(intent, 1);
                    }
                });
    }else{
        ActivityCompat.requestPermissions(IntroductionActivity.this, permissionsList.toArray(new String[permissionsList.size()]), 0);

    }
}

If still have problem, please try to change your file path :

String fileName="mytest5.txt";
 File folder = new File(Environment.getExternalStorageDirectory().getPath() + "/com.gvm.externalstorage.externalstoragetest/");

    if (!folder.exists()) {
        try {
            folder.mkdirs();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Default Save Path Creation Error:" + folder);
        }
    }

    File logFile = new File(folder, fileName);
    if (!logFile.exists()) {
        try {
            logFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Default Save Path Creation Error:" + logFile);

        }
    }

Best regards. I hope this can help u



回答4:

@Anvesh Another reliable method i'm using:

/**
 * Get external storage path use reflect on android 6.0 device.
 * Source code:
 * https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/storage/StorageVolume.java
 *
 * @param removable the sdcard can remove or not, true means external sdcard, false means
 *                  internal sdcard.
 * @return path of sdcard we want
 */
public static String getStoragePath(boolean removable) {
    WinZipApplication application = WinZipApplication.getInstance();
    Context mContext = application.getApplicationContext();
    StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
    Class<?> storageVolumeClazz = null;
    try {
        storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
        Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
        Method getPath = storageVolumeClazz.getMethod("getPath");
        Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
        Object result = getVolumeList.invoke(mStorageManager);
        final int length = Array.getLength(result);
        for (int i = 0; i < length; i++) {
            Object storageVolumeElement = Array.get(result, i);
            String path = (String) getPath.invoke(storageVolumeElement);
            boolean mRemovable = (Boolean) isRemovable.invoke(storageVolumeElement);
            if (removable == mRemovable) {
                return path;
            }
        }
    } catch (Exception e) {
        return null;
    }
    return null;
}


回答5:

After a lot of research I found ABSOLUTE SOLUTION. IT WORKS.

public boolean checkStorage() {
    File[] fs = con.getExternalFilesDirs(null);
    if (fs.length == 2)
        return true;
    else
        return false;
}