1

I have a huge database of about 150mb. Can I put the compressed version of the database e.g. zip in the asset folder for room to use or is that not possible?

PS: android studio apk compression is not sufficient enough

1 Answers1

2

First you need a function which can unzip archive to a some directory:

// unzip(new File("/sdcard/whatToUnzip.zip"), new File("/toThisFolder"));
fun unzip(zipFile: File, targetDirectory: File) {
    unzip(BufferedInputStream(FileInputStream(zipFile)), targetDirectory)
}

fun unzip(zipInputStream: InputStream, targetDirectory: File) {
    try {//BufferedInputStream(zipFileStream)
        ZipInputStream(zipInputStream).use { zipInput ->
            var zipEntry: ZipEntry
            var count: Int
            val buffer = ByteArray(65536)
            while (zipInput.nextEntry.also { zipEntry = it } != null) {
                val file = File(targetDirectory, zipEntry.name)
                val dir: File? = if (zipEntry.isDirectory) file else file.parentFile
                if (dir != null && !dir.isDirectory && !dir.mkdirs()) throw FileNotFoundException(
                    "Failed to ensure directory: " + dir.absolutePath
                )
                if (zipEntry.isDirectory) continue
                FileOutputStream(file).use { fileOutput ->
                    while (zipInput.read(buffer).also { count = it } != -1) fileOutput.write(
                        buffer,
                        0,
                        count
                    )
                }
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

I got it out of that stackoverflow's thread. Please read a thread to get more details. Then I added two method to work with a file from app's asset folder:

fun unzipAsset(assetsFilePath: String, context: Context, targetDirectory: File) {
    unzip(context.assets.open(assetsFilePath), targetDirectory)
}

fun Context.unzipAsset(assetsFilePath: String, targetDirectory: File) = unzipAsset(
    assetsFilePath,
    this,
    targetDirectory
)

Now we can unzip file to folder. To avoid copying an unzipped db file by room when I use createFromAsset or createFromFile methods of Room.databaseBuilder I want to unzip file to apps databases folder which used by room to store db file. That why I need additional methods to get db folder path and to check when db file already exist:

fun Context.databaseFolderPath(): File? = this.getDatabasePath("any.db").parentFile

// name – The name of the database file.
fun Context.isRoomDbFileExist(name: String): Boolean  {
    return this.getDatabasePath(name)?.exists() ?: false
}

And now, how to use all thinks together:

abstract class AppDatabase : RoomDatabase() {

companion object {

        private const val DB_NAME = "sunflower-db"

        // Create and pre-populate the database. See this article for more details:
        // https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
        private fun buildDatabase(context: Context): AppDatabase {
            if(!context.isRoomDbFileExist(DB_NAME)) {
                // unzip db file to app's databases directory to avoid copy of unzipped file by room
                context.unzipAsset("sunflower-db.zip", context.databaseFolderPath()!!)
                // or unzip(File("your file"), context.databaseFolderPath()!!)
            }
            return Room.databaseBuilder(context, AppDatabase::class.java, DB_NAME)
                //.createFromAsset(DB_NAME) // not zipped db file
                .build()
        }
    }
}

I test this code on nice open source project - sunflower. Next I want to show screen with project structure , where sunflower-db.zip located:

enter image description here

The approach above works but You shouldn't take this sample as right or best solution. You should to think about avoid unzipping process from main thread. May be will be better if you implement your own SupportSQLiteOpenHelper.Factory(look like complicated).

Artem Viter
  • 804
  • 7
  • 12