Search code examples
kotlinandroid-sqlite

Where in Kotlin Android project folder to copy a SQLite database file?


I want to ship a SQLite database packed with my app, within Android App Bundle because my app needs it to function. I'm not sure if this is possible at all or app needs to download the data via internet after being installed on Android?

However, if it can be done, I need to know is there a proper path to copy database file, maybe something like 'MyApp\app\src\main\java\com\example\MyApp\databases', I dunno?

Thanx in advance!


Solution

  • You copy the database file into the assets folder(directory) or if you wish the databases folder in the assets folder(directory).

    You may need to create the folders. You can do this by right clicking App selecting New/Directory and then selecting or typing src\main\assets (databases after this if you want the databases folder).

    You then need to copy the file from the assets (or assets/databases) folder into the location (folder/directory) from which you want to access the database. This is typically data/data/the_package_name/databases which can be ascertained using the Context's getDatabasePath method.

    • The asset is part of the package, it can only be read and furthermore it is compressed. Hence the reason why you must copy it from the assets.

    Note that before the copy you should check that the databases folder/directory exists and if not then use the mkdir or mkdirs method(function) to create the directory.

    The copy should be done before trying to access the database.

    Example

    Here's the database in the assets folder:-

    enter image description here

    using this then here's an example database helper class that extends the SQLiteOpenHelper class, uses a singleton approach, and includes code to:-

    • see if the database exists as function ifDatabaseExists (which if the database doesn't exist will also create the databases directory if it doesn't exist)
    • get and copy the database from the asset folder as function getAndCopyAssetDatabase (not that this invokes the ifDatabaseExists function).
    • get an instance, the only one i.e. a singleton, of the database helper DBHelper
    • it should noted that getting an instance DOES NOT open/connect to the database (so just getting the instance will not invoke the asset copy)

    Here's the DBHelper class:-

    const val DATABASE_NAME = "the_database.db" /* the database name */
    const val ASSET_NAME = "the_database.db" /* The name of the asset file which could be different if required */
    const val DATABASE_VERSION = 1
    const val ASSET_COPY_BUFFER_SIZE = 8 * 1024
    class DBHelper: SQLiteOpenHelper {
    
        private constructor(context: Context) : super(context, DATABASE_NAME,null, DATABASE_VERSION)
    
        companion object {
            private var instance: DBHelper?=null
            fun getInstance(context: Context): DBHelper {
                if (this.instance==null) {
                    getAndCopyAssetDatabase(context)
                    instance = DBHelper(context);
                }
                return instance as DBHelper
            }
    
            private fun ifDatabaseExists(context: Context): Boolean {
                val dbFile = context.getDatabasePath(DATABASE_NAME)
                if (dbFile.exists()) return true
                else if (!dbFile.parentFile.exists()) {
                    dbFile.parentFile.mkdirs()
                }
                return false
            }
    
            private fun getAndCopyAssetDatabase(context: Context) {
                if (ifDatabaseExists(context)) return
                context.assets.open(ASSET_NAME).copyTo(
                    FileOutputStream(context.getDatabasePath(DATABASE_NAME)),
                    ASSET_COPY_BUFFER_SIZE
                )
            }
        }
    
        override fun onCreate(p0: SQLiteDatabase?) {
            // should not do anything if using a pre-packaged database
        }
    
        override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
            // May or may not be used
        }
    }
    

    To use this and force an open/connection to the database then :-

    class MainActivity : AppCompatActivity() {
        lateinit var db: DBHelper
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            db = DBHelper.getInstance(this)
            db.writableDatabase /* Force Database Access (open) */
        }
    }
    

    Result (new Install of the App) (via App Inspection):-

    enter image description here

    • obviously not your database, just an arbitrary one used to demonstrate.