Pull to refresh

Проблема с чтением файлов более 1Мб в Android

Development for Android
Sandbox
Добрый день.

При написании одного приложения для Android столкнулся с проблемой загрузки файлов из директории assets размер которых превышает 1Мб.
Допустим, в папке проекта, в assets лежит файл file.dat, который надо загружать в память и читать данные из него.

image

Открываем файл, и читаем в buffer 8 первых байт

AssetManager am = context.getAssets();
InputStream is = am.open("file.dat", AssetManager.ACCESS_BUFFER);
// Read file
is.read(buffer, 0, 8);


В результате выполнения кода на Android 2.2 и ниже, получаем сообщение в LogCat
06-14 17:10:48.554: DEBUG/asset(382): Data exceeds UNCOMPRESS_DATA_MAX (1048852 vs 1048576)
и IOException
06-14 17:10:48.654: WARN/System.err(698): java.io.IOException

Смотрим исходники андроида. В файле frameworks/base/include/utils/Asset.h находится та самая константа UNCOMPRESS_DATA_MAX

enum {
/* data larger than this does not get uncompressed into a buffer */
#ifdef HAVE_ANDROID_OS
UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024
#else
UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024
#endif
};


А в файле frameworks/base/include/utils/Asset.cpp происходит проверка на размер файла

if (mUncompressedLen > UNCOMPRESS_DATA_MAX) {
LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n",
(long) mUncompressedLen, UNCOMPRESS_DATA_MAX);
goto bail;
}


Как видно из комментария, данные, превышающие по размеру UNCOMPRESS_DATA_MAX в байтах, не распаковываются в буфер из .apk файла.

Решить проблему можно несколькими способами:
1. Самый простой — изменить расширение файла file.dat, на такое, что этот файл не будет сжиматься при упаковке в .apk файл. Список этих расширений определён в Package.cpp

/* these formats are already compressed, or don't compress well */
static const char* kNoCompressExt[] = {
".jpg", ".jpeg", ".png", ".gif",
".wav", ".mp2", ".mp3", ".ogg", ".aac",
".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
".amr", ".awb", ".wma", ".wmv"
};


Либо, можно вручную скомпилировать проект с помощью aapt с опцией "-0", указав расширения файлов, которые не надо сжимать в .apk. Если использовать пустую строку, вместо расширения, ни один файл не будет сжат.
Этот способ плох тем, что размер загружаемого пользователем установочного файла из маркета значительно вырастает (на разницу между сжатым и несжатым file.dat), а объём файла довольно критичен для мобильных устройств. В моём случае, в file.dat хранится текстовая информация, используемая в приложении, в нераспакованном виде занимает немного более 1Мб, а в .apk файле сжимается до 40Кб.
Если ваши файлы из assets плохо сжимаются в zip (медиа), этот способ отлично подойдёт.

2. Второй способ заключается в разбиении file.dat на кусочки по 1Мб, например из консоли:
split file.dat --bytes=1024K

Получившиеся файлы нужно поместить в assets, при первом запуске приложения «склеить» по кусочкам и поместить в директорию с приложением. Данный способ хорошо описан на stackoverflow, его предложил человек под именем Seva Alekseyev, поэтому приведу только ссылку stackoverflow.com/questions/2860157/load-files-bigger-than-1m-from-assets-folder/3093966#3093966
В этом случае, ваше приложение будет хранить две копии данных, одни в .apk файле, другие, выгруженные в директорию с приложением. Также добавляется немного лишнего кода и необходимость следить за существованием и целостностью «распакованных» данных.

Описанной проблемы для Android 2.3.1 и выше нет.

Конечно, можно придумать ещё что-то, например, загрузка файлов по сети (т.н. кэш), что не всегда надёжно, если пользователь отключил интернет, или генерация файла с нуля, но мне двух вышеописанных методов хватило.
Tags:android developmentassetsioexeption
Hubs: Development for Android
Total votes 43: ↑40 and ↓3+37
Views3.5K

Popular right now