Быстрое руководство по работе с assets в Android

Коротко: читайте файлы из assets через AssetManager (context.assets) и копируйте их потоками в внутреннюю (context.filesDir или context.getDatabasePath) или внешнюю память (getExternalFilesDir / MediaStore) с учётом Scoped Storage и прав доступа.

Что такое assets и когда их использовать

Папка assets хранит "сырые" файлы (JSON, шрифты, SQLite, изображения и т.д.), сохраняет структуру подпапок и доступна только через AssetManager. Используйте assets для предустановленных небольших данных (конфиги, словари, шрифты). Большие файлы лучше грузить динамически или хранить в res/raw.

Не помещайте в assets очень большие файлы (рекомендно <1 МБ в APK). Для больших бинарных файлов — используйте res/raw, загрузку по сети или app‑specific external storage.

Чтение файлов из assets (Kotlin и Java)

Используйте context.assets.open(name) и читайте поток. Для текстовых файлов — bufferedReader.readText(), для бинарных — работайте с байтами/потоком.

Kotlin — текст:

fun readAssetText(context: Context, name: String): String? =
    try {
        context.assets.open(name).bufferedReader().use { it.readText() }
    } catch (e: IOException) {
        e.printStackTrace(); null
    }

Java — текст:

public String readAssetText(Context ctx, String name) {
    try (InputStream in = ctx.getAssets().open(name);
         BufferedReader r = new BufferedReader(new InputStreamReader(in))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = r.readLine()) != null) sb.append(line).append("\n");
        return sb.toString();
    } catch (IOException e) { e.printStackTrace(); return null; }
}

Для чтения бинарного файла:

context.assets.open("image.png").use { input ->
    val bytes = input.readBytes() // избегайте для очень больших файлов
}

Копирование из assets в память устройства

Assets — только для чтения в APK. Копируйте при первом запуске или по требованию в нужную папку.

Копирование во внутреннюю память (рекомендуется — приватно для приложения):

fun copyToInternal(context: Context, assetName: String, destName: String): File? {
    val dest = File(context.filesDir, destName)
    return try {
        context.assets.open(assetName).use { input ->
            dest.outputStream().use { output -> input.copyTo(output) }
        }
        dest
    } catch (e: IOException) { e.printStackTrace(); null }
}

Для SQLite копируйте в context.getDatabasePath("mydb.db") и затем открывайте БД — Android применит WAL при необходимости.

Копирование во внешнюю app‑specific директорию (без разрешений):

val dir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
val dest = File(dir, "myfile.dat")
if (!dest.exists()) {
    context.assets.open("asset.dat").use { input -> dest.outputStream().use { input.copyTo(it) } }
}

Для публичных директорий на Android 10+ используйте MediaStore или запросы разрешений/SAF; WRITE_EXTERNAL_STORAGE устаревает и на новых версиях требует осторожности.

Не предполагается, что assets-файлы можно изменять на месте. Также имена файлов в assets чувствительны к регистру.

Работа с подпапками, перечисление и большие файлы

  • Открытие в подпапке: assets.open("folder/file.json").
  • Получение списка: context.assets.list("folder") — возвращает массив имён.
  • Для больших файлов читайте и копируйте потоками (буфер ~8KB), не загружайте весь файл в память.

Частые ошибки

  • FileNotFoundException — неверный путь или регистр имени.
  • OutOfMemoryError — чтение больших файлов через readBytes(); используйте по‑частям.
  • Проблемы с внешней памятью — не учтён Scoped Storage и отсутствуют нужные разрешения.

FAQ

  • Нужно ли запрашивать разрешения для getExternalFilesDir()? — Нет, это app‑specific и разрешений не требует.
  • Как проверить целостность скопированного файла? — Считайте SHA-256 хеш и сравните с эталоном.
  • Как часто копировать данные из assets? — Лениво: при первом запуске или при обновлении версии/контента.

Лучшие практики: копируйте лениво, проверяйте наличие и хеш, тестируйте на реальных устройствах с разными версиями Android и поведением SD-карты.