Папка Android KitKat 4.4 на SD-карте
Мы только что испортили новые разрешения, которые применяются для записи файлов на SD-карты (внешнее хранилище) на Android 4.4 (EACCES Permission Denied)
До KitKat мы устанавливаем нашу записываемую папку следующим образом:
mfolder = Environment.getExternalStorageDirectory().getPath() + "/appfiles";
Однако после нескольких часов поиска я пришел к выводу, правильно или неправильно, что на 4.4 устройствах, позволяющих записывать файлы, это необходимо изменить:
mfolder = Environment.getExternalStorageDirectory().getPath() + "/Android/data/com.xyz.abc/appfiles";
Итак, mfolder будет что-то вроде:/mnt/sdcard/Android/data/com.xyz.abc/appfiles
Правильно ли это, создаем ли мы такую папку на SD-карте, чтобы включить 4.4 устройства для записи файлов?
mfolder - это строка, которую мы сохраняем для общих настроек.
Тогда у нас есть этот код, который запускается один раз, если API >= 19, который изменяет строку mfolder String, а затем копирует все файлы из старой папки в новую папку "kitkat".
if (android.os.Build.VERSION.SDK_INT>=19){
if (!mfolder.contains("/Android/data/com.xyz.abc/appfiles")){
if (prefs.getBoolean("kitkatcheck", false)==false){
//update mfolder from
// /mnt/sdcard/appfiles
// to
// /mnt/sdcard/Android/data/com.xyz.abc/appfiles
String prekitkatfolder = mfolder;
String kitkatfolder = mfolder.replace("/appfiles", "/Android/data/com.xyz.abc/appfiles");
mfolder = kitkatfolder;
try {
File sd = new File(mfolder);
if(!sd.exists() || !sd.isDirectory()) {
sd.mkdirs();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), "Error creating Kitkat folder!\n" + e.toString(), Toast.LENGTH_LONG).show();
return;
}
prefEditor.putString("patternfolder", mfolder);
prefEditor.putBoolean("kitkatcheck", true);
prefEditor.commit();
//copy files and folder from old appfiles folder to new.
AllFiles.clear();
listFilesAndFilesSubDirectories(prekitkatfolder);
if (AllFiles.size()>0){
for (File child : AllFiles ) {
try {
File dest = new File(child.toString().replace(prekitkatfolder, kitkatfolder));
try {
String filePath = dest.getPath().substring(0, dest.getPath().lastIndexOf(File.separator));
File subfolder = new File(filePath);
if(!subfolder.exists() || !subfolder.isDirectory()) {
subfolder.mkdirs();
}
} catch (Exception ex) {
}
copyFile(child, dest);
} catch (Throwable t) {
}
}
}
}
}
Затем я уведомляю пользователя о том, что их файлы были скопированы в новую папку, и что из-за новых разрешений им придется вручную удалить старую папку prekitkatfolder. Я предполагаю, что они смогут это сделать только в том случае, если у них есть менеджер файлов запаса или если они отмонтировали SD-карту и поместили ее на ПК из-за новых разрешений 4.4?
Кроме того, для нас кажется, что эти права доступа не затрагивают всех наших пользователей с помощью Kitkat. Некоторые из них могут записывать исходное местоположение папки на своем внешнем хранилище, а некоторые получают ошибку EACCES (Permission Denied). Может ли кто-нибудь рассказать о том, почему это может быть, можно было бы подумать, что это применимо ко всем 4.4 устройствам, использующим внешнее хранилище?
Поскольку у нас нет реального устройства 4.4, нам приходится тестировать этот код с помощью эмулятора (API 19), но мы не получаем ошибку EACCES Permission Denied. Итак, мы выпустили бета-версию с кодом выше и сказали, что скопированные файлы оказались во внутреннем хранилище, как это может быть?
Любые идеи, что мы делаем неправильно, заранее спасибо
Ответы
Ответ 1
Обновленное решение.
Это устанавливает и также создает папку в правильном месте для KitKat.
mfolder = this.getExternalFilesDir("asubfoldername").getAbsolutePath();
Однако это не является полным доказательством, если Android-устройство имеет как внутренние, так и внешние вторичные места хранения, выше будет использовать внутренний. Не совсем то, что мы хотим, поскольку нам нужен путь к съемной SD-карте или, еще лучше, путь к вторичной ячейке хранения с самым свободным доступным пространством.
File[] possible_kitkat_mounts = getExternalFilesDirs(null);
Обратите внимание на "s" в конце getExternalFilesDirs. Это создает массив вторичных внешних хранилищ.
for (int x = 0; x < possible_kitkat_mounts.length; x++) {
//Log.d("test", "possible_kitkat_mounts " + possible_kitkat_mounts[x].toString());
boolean isgood=false;
if (possible_kitkat_mounts[x] != null){
isgood = test_mount(possible_kitkat_mounts[x].toString());
if (isgood==true){
arrMyMounts.add(newymounts(Device_get_device_info(possible_kitkat_mounts[x].toString()), possible_kitkat_mounts[x].toString()));
}
}
}
//sort arrMyMounts size so we can use largest
Collections.sort(arrMyMounts, new Comparator<mymounts>(){
public int compare(mymounts obj1, mymounts obj2){
return (obj1.avaliablesize > obj2.avaliablesize) ? -1: (obj1.avaliablesize > obj2.avaliablesize) ? 1:0 ;
}
});
if (arrMyMounts.size()>0){
mfolder = arrMyMounts.get(0).name + "/asubfoldername";
//Log.d("test", "selected kitkat mount " + kitkatfolder);
}else{
//do something else...
}
Из массива возможных_kitkat_mounts мы проверяем через test_mount, чтобы увидеть, можем ли мы фактически записать выбранное местоположение, и, если это удастся, мы добавим это местоположение в arrMyMounts.
Посредством сортировки arrMyMounts мы можем получить местоположение с наиболее доступным свободным пространством.
Hey presto, arrMyMounts.get(0).name - это дополнительное хранилище kitkat с самым свободным пространством.
Ответ 2
Google заблокировал доступ на запись к внешним устройствам хранения данных в Android 4.4. Пока они не изменят его, нет способа вернуть его обратно без root.
Дополнительная информация: https://groups.google.com/forum/#!msg/android-platform/14VUiIgwUjY/UsxMYwu02z0J
Возможно, он работает на некоторых устройствах с Kitkat, у которых есть слот для карт miniisd. Это сбивает с толку: (