Отримання шляху до карти пам'яті SD Card на Android +14
- 03.04.15 7:21 •
- vait •
- # 254813 •
- Хабрахабр •
- 16 •
- 11897
- такий же як Forbes, тільки краще.
Розробляючи додаток для проведення змагань, я зіткнувся з проблемою зберігання бази даних. Проблема полягала в тому, як мені визначити зовнішню карту пам'яті. В цілому пошук в мережі точної відповіді не дав. Тому, об'єднавши всі знайдені результати, я зібрав свій клас. Якщо кому цікаво, дивимося під катом.
Отже, почнемо з теорії.
Термінологія
До версії KitKat 4.4 API не надавало функціоналу для отримання шляхів до зовнішньої пам'яті. Починаючи з цієї версії (API 19) з'явилася функція public abstract File [] getExternalFilesDirs (String type), яка повертає масив рядків з шляхами до внутрішньої і зовнішньої пам'яті. Але як же бути з нашої SD Card, яка вставлена в слот? Шлях до неї ми знову не можемо отримати.
Результати пошуку
Щоб відповісти на поставлене запитання я звернувся до всезнаючого Гуглу. Але і він мені не дав чіткої відповіді. Було розглянуто безліч варіантів визначення від використання стандартних функцій, які ведуть до зовнішньої пам'яті, але нічого спільного з видаляються пристроями зберігання даних вони не мають, до обробки правил монтування пристроїв (Android же на ядрі Linux працює). В останніх випадках були використані «зашиті» шляху до папки з прімонтірованимі пристроями (в різних версіях ця директорія різна). Не варто забувати, що від версії до версії правила монтування змінюються.
В кінцевому підсумку я вирішив об'єднати всі отримані знання і написав свій клас, який може нам повернути шляху до зовнішніх і видаляється пристроїв.
опис коду
Був створений клас MountDevice. який містить в собі шлях до пристрою, тип пристрою і якийсь хеш.
Типів пристроїв виділено два (внутрішню пам'ять я не став чіпати, так як до неї доступ можна отримати через API системи).
І був створений клас StorageHelper. який і здійснює пошук доступних карт пам'яті.
У класі StorageHelper реалізовано два способи пошуку - через системне оточення (Environment) і з використанням утиліти Linux mount. а точніше результату її виконання.
Спосіб перший - Environment
При роботі з оточенням я використовую стандартну функцію getExternalStorageDirectory () для отримання інформації про зовнішній пам'яті. Щоб отримати інформацію про видаляється пам'яті, я використовую змінну оточення "SECONDARY_STORAGE".
Зовнішня пам'ять завжди одна і зазвичай завжди є, тому перевіряємо її на читаність, обчислюємо хеш і запам'ятовуємо. Видаляється пам'яті може бути багато, тому необхідно отриманий рядок розбити по разделителю і перевіряти кожне значення.
Варіант рішення взято зі stackoverflow. Відповідь десь там внизу.
Спосіб другий - mount
Так як у мене довго не виходило змусити систему мені сказати шлях до видаляється пам'яті, я вирішив шукати в сторону примонтировать пристроїв. В системі є файли конфігурації, в яких описані правила монтування зовнішніх пристроїв. Все б добре, але на Android версії 4. * до цього файлу простим смертним доступу немає, тому розглядати цей спосіб не буду.
Повернемося до утиліти mount. При запуску без параметрів команда повертає список змонтованих файлових систем. Видаляються пристрої мають зазвичай формат файлової системи FAT, то будемо виділяти рядки, в яких є характеристика "fat". Зовнішня пам'ять буде характеризуватися параметром "fuse".
Примітка: при використанні такого способу не завжди коректно (швидше за все я щось не врахував) визначаються типи смотнтірованних пристроїв. Різницю помічав на різних версіях Android. Тому цей спосіб можна використовувати як додатковий.
Варіант рішення взято зі stackoverflow. Відповідей там кілька приблизно однакових.
про дублювання
Багато хто помічав в директорії монтування пристроїв таку картину:
І що саме цікаво, все це одна і та ж зовнішня карта пам'яті. Таке дроблення починається з версії Jelly Bean і зроблено це для підтримки багато режиму роботи системи. Більш детально тут. І ось, щоб не отримувати одну й ту ж саму карту пам'яті як різні пристрої, необхідний спосіб визначення ідентичності. Якби був доступ до конфігурації монтування, то і питань не було. Але доступу немає. Тому я тут підглянув рішення з розрахунком хеша для кожного пристрою:
- створюємо StringBuilder
- записуємо в нього загальний розмір пристрою і частинка простору пристрою
- обходимо вміст кореня пристрої
- записуємо ім'я каталогу
- записуємо ім'я файлу і розмір
- обчислюємо hash
Своя функція розрахунку хеша calcHash
приклад використання
висновок
Докладні міркування з цього питання розуміння пам'яті в Android, деякі поради можна прочитати тут.
Вихідний код всього класу розташований ще ніде не розташований. Днями постараюся розмістити на gitHub.
Хто ще якими способами користується?
UPD1: Вихідний код класу на bitbucket
Зараз це не дуже актуально, але в теорії, карти пам'яті більше, ніж вбудована «зовнішня» пам'ять. Тому потенційно велику базу краще зберігати на карті пам'яті. Я з таких міркувань став шукати альтернативу.
В даний час у мене LG L65. У нього заявлено 4 ГБ пам'яті, з них користувачеві доступно всього 1,5. Ставимо додатки, мелодії для дзвінка і т.п. і залишається всього 200 Мб.
UPD. Viber, наприклад, все зберігає у зовнішній пам'яті. Відповідно місце постійно зменшується.
Я думаю вайбер зберігає там тільки об'ємні медіа-файли, а не всю переписку. Може бути саме для великої кількості відносно великих файлів це і актуально, але точно не для «потенційно великий бази».
1) Наскільки повинна бути велика база щоб getExternalStorageDirectory () не задовольняло повністю? (Я сподіваюся ви медіа-файли в blob-ах не зберігайте)
2) Якщо база дійсно велика, то вона зберігає величезну кількість записів. Я не думаю, що варто світити ці записи всім користувачам (навіть без рута).
Я ось теж не розумію, але скільки було обурених користувачів які кричали щоб гра скачував ресурси саме на флешку, а не в Environment.getExternalStorageDirectory () - ми поки не зробили мали тисячі одиниць у відгуках через це. І до речі, всі вищеописані шаманства все одно не рятують і будуть екзотичні девайси в яких визначити вірно не вийде - тому ще й можливість руками ввести шлях додали.
Ем ... Мені здається або спеціально для цього є Expansion File?
The specific location for your expansion files is:
Не можу зрозуміти невдоволення ваших користувачів. Цілком нормальна практика.
obb не допоможуть - на тих аксесуарах на яких getExternalStorageDirectory вказує на внутрішню пам'ять при наявності SD картки, obb теж будуть гойдатися на внутрішню пам'ять
Наприклад на моєму апараті в папці / mnt / є папка «sdcard» - вона посилається на зовнішню пам'ять. Карти пам'яті в / mnt / НЕ примонтировать. Мені здається, це не найкращий спосіб.
Чудовий і найкращий, на мій погляд, варіант був би через карту «vold.fstab». Але на жаль, на останніх версіях андройда (з 4.3) доступ до цього файлу без рута не одержати. Та й називається він по-іншому, і розташований в іншому місці:
For Android 4.2.2 and earlier, the device-specific vold.fstab configuration file defines mappings from sysfs devices to filesystem mount points
For Android releases 4.3 and later, the various fstab files used by init, vold and recovery were unified in the / fstab.
Джерело: source.android.com