76

I'd like to use Room with a pre-populated database, but I can't understand how to tell Room where to find my database.

I've now put it in src/main/assets/databases and when I create the instance for the Room database I create it this way:

Room.databaseBuilder(
    getApplicationContext(),
    AppDatabase.class,
    "justintrain.db"
)
.allowMainThreadQueries()
.build();

This way tho, I think it's creating a new database every time, or anyways, it's not using the pre-populated one.

How can I make it to find my database?

MikeT
  • 51,415
  • 16
  • 49
  • 68
Alberto Giunta
  • 1,398
  • 1
  • 13
  • 17
  • 6
    I haven't seen a good solution for this. I filed [a feature request](https://issuetracker.google.com/issues/62185732) for it. – CommonsWare May 30 '17 at 14:07
  • @CommonsWare so it'll take a while until it will be implemented I guess.. do you know any workaround that could be used until then? (Thanks a lot anyway for reporting it!) – Alberto Giunta May 30 '17 at 14:25
  • 2
    Well, you could assume that the database is being stored in `getDatabasePath()` for your chosen database filename. Then, arrange to copy the asset into that path before creating the `RoomDatabase`, if that file does not already exist. This is the first option I propose in that issue, where ideally we have more assurances that "`getDatabasePath()` for your chosen database filename" is the right answer. – CommonsWare May 30 '17 at 14:39
  • @AlbertoGiunta does this work for you https://medium.com/google-developers/understanding-migrations-with-room-f01e04b07929 ? – Gregriggins36 Aug 03 '17 at 15:32
  • have you tried passing the full path of the database in the constructor? this used to work with SqliteOpenHelper – Mohammad Yahia Aug 06 '17 at 18:17
  • 1
    CommonsWare has a good solution here: https://github.com/commonsguy/cw-androidarch/tree/v0.6/General/AssetRoom – live-love Jan 01 '18 at 18:19
  • I found this helpful https://www.meekcode.com/blog/basic-mvvm-in-android-kotlin-using-prepopulated-database-and-room-with-unit-testing-part-1 – ikmazameti Aug 20 '21 at 09:48

8 Answers8

43

This is how I solved it, and how you can ship your application with a pre-populated database (up to Room v. alpha5)

  • put your SQLite DB database_name.db into the assets/databases folder

  • take the files from this repo and put them in a package called i.e. sqlAsset

  • in your AppDatabase class, modify your Room's DB creation code accordingly:

    Room.databaseBuilder(context.getApplicationContext(), 
                         AppDatabase.class, 
                         "database_name.db")
    .openHelperFactory(new AssetSQLiteOpenHelperFactory())
    .allowMainThreadQueries()
    .build();
    

Note that you have to use "database_name.db" and not getDatabasePath() or other methods: it just needs the name of the file.

Alberto Giunta
  • 1,398
  • 1
  • 13
  • 17
  • Hey Alberto, what program would you recommend for the .db file generation (I mean, database editor)? I need an editor that generates a database compatible with the Room schema. – Pedro Guerra Sep 28 '17 at 17:48
  • 7
    Hei @PedroGuerra! I used a really simple program named "DB Browser for SQLite". I think it's crossplatform so that should work well for your needs! – Alberto Giunta Sep 29 '17 at 22:01
  • 1
    Works also with alpha9 (only need to implement method from `AutoClosable` where needed) but unable to do it with beta1 (`SupportSQLiteOpenHelper.Configuration` missing some stuff so `AssetSQLiteOpenHelper` cannot be instantiated) – makvasic Oct 02 '17 at 13:47
  • @AlbertoGiunta Hey Alberto, sorry for the request, but could you please create a new tag on github, so I can use jitpack to import it into my project? – Pedro Guerra Oct 04 '17 at 18:09
  • Hi, i can't use it in .kt – Frank Oct 24 '17 at 14:03
  • 12
    Thank for @Alberto Giunta It saves my time! I using your code and build to dependency to easy for use in here! https://github.com/daolq3012/AssetSQLiteOpenHelper Anyone can use this by easy way. – DaoLQ Nov 15 '17 at 07:27
  • Thank you @AlbertoGiunta, this solution still works on recent version of the Room library. – kato2 Feb 23 '18 at 10:34
  • I'm assuming you need to write migrations when updating the database schema using this prepopulated DB? Upon updating the schema, the database is always empty – Josh Laird Mar 26 '18 at 21:54
  • i need like something prepopulated database which i downloaded from google drive api and use it – M.Yogeshwaran Apr 11 '18 at 07:40
  • It's request Java 8? – AlexS Aug 16 '18 at 05:02
  • @young_boy; Is there a way to override the existing database when increasing the database version? – syloc Oct 12 '18 at 06:56
  • As I saw the issues on GitHub on both repos(didn't test it myself), the problem remains on updating the database it seems. – getsadzeg May 05 '19 at 22:18
33

UPDATE (Nov 7th 2019)

Room now supports using a pre-packaged database out of the box, since version 2.2.0

https://developer.android.com/jetpack/androidx/releases/room#2.2.0

Solution before version 2.2.0: Simple solution without any other external libraries.

Room relies on the existing Android framework code to create or open a database. If you look into the source code of FrameworkSQLiteOpenHelper (Room's version of SQLiteOpenHelper) it internally calls SQLiteOpenHelper.getReadableDatabase() and other methods wherever needed.

So, the simplest solution is to just copy the DB file from assets directory to mContext.getDatabasePath("my-database.sqlite") before creating the DB with Room.

In your case, the code looks something like this -

private final String DB_NAME = "my-database.sqlite";

private MyDatabase buildDatabase(Context context) {
    final File dbFile = context.getDatabasePath(DB_NAME);

    if(!dbFile.exists()) {
        copyDatabaseFile(dbFile.getAbsolutePath());
    }

    return Room.databaseBuilder(context.getApplicationContext(),
        MyDatabase.class, DB_NAME)
        .build();
}

private void copyDatabaseFile(String destinationPath) {
    // code to copy the file from assets/database directory to destinationPath
}

This link has the code needed to copy the DB - link with code

Nishanth
  • 1,801
  • 21
  • 25
  • 4
    Thank you so much. This should be the accepted answer. I've spend a whole day trying to figure this out. I knew we do not need another library and that copying database file to database location should work. – Bugs Happen Jan 17 '19 at 11:27
  • Unfortunate that this answer is only 3rd place and probably missed by many. – Big_Chair Apr 22 '19 at 10:11
  • How would you write a test for that? – user1927033 May 21 '19 at 23:39
  • 1
    How do you tell Room to use the newly copied database? I can confirm through a file browser that the db is transferred correctly but Room.databasebuilder still generates an empty db. – DinosauRuss May 28 '19 at 18:09
17

I was having the same problem so I created a library which does exactly that. the accepted answer work but I think it's easier to use a library.

AppDatabase db = RoomAsset
    .databaseBuilder(context.getApplicationContext(), AppDatabase.class, "database_name.db")
    .build(); 

Add it to your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

Add the dependency

dependencies {
    // ... other dependencies
    implementation 'com.github.humazed:RoomAsset:v1.0'
}

you can find the library here: https://github.com/humazed/RoomAsset

humazed
  • 74,687
  • 32
  • 99
  • 138
15

Working 2019 solution without hacks or dependencies (Kotlin)

  1. Place your .db file in assets/databases (or really any folder in there, as long as it's under assets).

  2. Use Room 2.2's existing createFromAsset() function, passing in the path to the database. For example, if your database file is named my_data.db and is under the databases directory of the assets folder, then you would do createFromAsset("databases/my_data.db").

Assuming your database name (e.g., my_data) is stored in a constant variable named DATABASE_NAME, you can use this sample code:

Room.databaseBuilder(
                    context.applicationContext,
                    MyDatabase::class.java,
                    DATABASE_NAME
                )
                    .createFromAsset("databases/$DATABASE_NAME.db")
                    .build()

Important: Make sure the schema of your data class/entity precisely matches the schema of your .db file. For example, if a column isn't explicitly marked as NOT NULL in the .db file, then that means the column can have null values in it. In Kotlin, you would have to match that with val colName: dataType? = null in your data class. If you just do val colName: dataType, Kotlin will compile that to a NOT NULL column, and that will throw an exception when you try to run your app.

Note: If instead you want to create a Room database from a database file that you download onto the Android device itself, you can alternatively use the createFromFile() function. Check out the official documentation on how to do this.

  • But won't this override the database in the phone on every startup? How to check if the db already exists? – Big_Chair Apr 24 '22 at 13:30
11

Room now supports Prepopulated Databases. Just prepare your database by using a program like SQLite Browser or any other of your choice. Then put it in Assets Folder probably in a subfolder called database then call:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build()

If you did not provide your database as an Asset but you downloaded it or it is in File System then then the method is:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(File("mypath"))
.build()

For more description or database migrations about this Feature you can check the Documentation Training.

neowinston
  • 7,584
  • 10
  • 52
  • 83
Xenolion
  • 12,035
  • 7
  • 33
  • 48
  • 1
    Works Perfect. A Huge Thanks to @Xenolion You saved my many days. – GreenROBO Feb 06 '20 at 08:39
  • 2
    I just noticed, my Database is always being overridden by the one inside assets (every time I launch the app). Didnt anyone else had this issue? Using Room 2.2.5 – rimes Apr 21 '20 at 10:49
  • I know it, click here lets see if i can help you, text me there and delete your comment and i will delete mine https://wa.me/255684521543 – Xenolion Apr 21 '20 at 17:37
1

Similar solution with room without using external libraries: 1. Copy your database in assets folder 2. Copy your database from assets folder

public class MainActivity extends AppCompatActivity {

public static AppDatabase db;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    copyDatabase(getApplicationContext(), "yourdatabase.db");

    db = Room.databaseBuilder(getApplicationContext(), .class, "yourdatabase.db").allowMainThreadQueries().build();
}

private void copyDatabase(Context context, String databaseName) {
    final File dbPath = context.getDatabasePath(databaseName);

    // If the database already exists, return
    if (dbPath.exists()) {
        Log.d("Activity", "db Path Exists");
        return;
    }

    // Make sure we have a path to the file
    dbPath.getParentFile().mkdirs();

    // Try to copy database file
    try {
        final InputStream inputStream = context.getAssets().open(databaseName);
        final OutputStream output = new FileOutputStream(dbPath);

        byte[] buffer = new byte[8192];
        int length;

        while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
            output.write(buffer, 0, length);
        }

        output.flush();
        output.close();
        inputStream.close();
    }
    catch (IOException e) {
        Log.d("Activity", "Failed to open file", e);
        e.printStackTrace();
    }
}

}

dig
  • 247
  • 1
  • 7
1

Starting with Room 2.2, you can pre-populate your database using command below:

Room.databaseBuilder(appContext, TestDatabase.class, “Sample.db”)
    .createFromAsset(“database/myapp.db”)
    .build()
Tuan Dao
  • 99
  • 5
0

you just copy assets/databases to app/databases
and than add addMigrations() in databaseBuilder
it will keep your data