Android 6.0 写入外置SD卡

Android 6.0 Write to external SD Card

我尝试了在 Android 6.0 上写入外部 SD 卡的所有方法,但我无法在上面写入。

我研究了 Whosebug 并发现了很多东西,但 none 有效。这是我的代码

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

此代码适用于 Android 5.0,但不适用于 Android 6.0。

然后我也尝试了这个路径,这给了我权限错误,我也为运行时权限设置了所有权限和托管代码。

/mnt/media_rw/6AC9-083B

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

如果有人能帮助我,那就太好了,因为我从过去 3 天开始就在尝试这个。

谢谢, 安维什

从 API 23+(6.0) 开始,您需要请求 read/write 权限,即使它们已经在您的清单中,称为 Requesting Permissions at Run Time

来自文档

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" />

我觉得你应该先检查你的应用权限,确保你的存储权限已经打开。

如果没有存储权限: 请检查您是否在 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"/>

如果存储权限已关闭: 请检查您的运行时权限,也许您可​​以参考此代码

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

    }
}

如果仍然有问题,请尝试更改您的文件路径:

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

        }
    }

此致。希望对你有所帮助

经过长时间的努力,我想出了一个解决方案。在 Android 6.0 中,它不会总是使用这个给你 SD 卡路径:

System.getenv("SECONDARY_STORAGE") 

或这个

Environment.getExternalStorageDirectory()

所以我使用这个检索了外部 SD 卡路径

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

其余的一切都将保持不变以获得许可和所有。

感谢那些帮助过我的人。

@Anvesh 我正在使用的另一种可靠方法:

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

经过大量研究,我找到了绝对的解决方案。有效。

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