Use a pre-populated sqllite db file with Room [duplicate] - android

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?

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.

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

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

Working 2019 solution without hacks or dependencies (Kotlin)
Place your .db file in assets/databases (or really any folder in there, as long as it's under assets).
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.

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.

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();
}
}
}

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()

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

Related

How to change the default database file location of Room DataBase?

I am using RoomDB in my app. able to perform crud operations.
Actually i want to see the db file.
getDatabasePath("user.db").getAbsolutePath();
above code is giving me the directory where the db file is saves
directory is like this
/data/data/com.example.manvish.roomdb/databases/user.db
but still i am unable to access the data directory even using sudo from command prompt.
now i want to change the DB file location to some other folders in internal memory or SD card. how can i do this?
Java solution:
Grant permissions in Manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Create db (Here SofDatabase is a singleton class)
private static final String DB_NAME = "stack_overflow_db";
private static final String DB_PATH = String.format("%s/%s",
Environment.getExternalStorageDirectory().getAbsolutePath(), DB_NAME);
public static synchronized SofDatabase getInstance(Context aContext) {
if (sInstance == null) {
sInstance = Room.databaseBuilder(aContext, SofDatabase.class, DB_PATH)
.fallbackToDestructiveMigration()
.addCallback(roomCallback).build(); //adding callback from Room
}
return sInstance;
}
Callback
/**
* Get Notified once db is created
*/
private static final RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
Log.i("SOF", db.getPath()); //which prints out --> I/SOF: /storage/emulated/0/stack_overflow_db
// add some jobs once db is created
}
};
To change the db location just put the path when you build the Room database, an example for a db in your folder, in the internal storage:
fun getDatabase(context: Context, scope: CoroutineScope): TestDatabase {
return INSTANCE ?: synchronized(this) {
val instance =
Room.databaseBuilder(
context.applicationContext,
TestDatabase::class.java,
Environment.getExternalStorageDirectory().absolutePath + "/yourFolder/yourdb"
).build()
INSTANCE = instance
return instance
}
}
This is a Kotlin example, if you need it in Java, just let me know.
Regards.
So guys I'm posting my solution because I got stuck for several hours on this problem and I was almost convinced that we couldn't use a database that is located elsewhere than the "/data/data" internal directory of the application with Room
The solution is however very simple. with the following code we have the IllegalArgumentException exception: "File ... contains a path separator"
private fun buildDatabase(context: Context) : RMSRoomDatabase {
val packageName: String = context.getApplicationInfo().packageName
var path = "sdcard/Android/data/$packageName/files/rms_database.sqlite"
var builder = Room.databaseBuilder(
context,
RMSRoomDatabase::class.java,
path
)
return builder.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build()
}
But by simply changing the path with an slash as the first character, everything works correctly!
var path = "/sdcard/Android/data/$packageName/files/rms_database.sqlite"
Not sure if this is what you are looking for but in my case just like #M.Yogeshwaran I needed to be able to setup an initial state of the database and work from there so I ended up doing this:
/**
* #param context
*/
public DatabaseService(Context context) {
File dst = context.getDatabasePath(DB_NAME);
dst.delete();
File src = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()+"/database.db");
try {
copy(src,dst);
} catch (IOException e) {
e.printStackTrace();
}
// Init the database singleton
db = Room.databaseBuilder(context, CrediforceDatabase.class, Constants.DB_NAME).build();
}
public static void copy(File src, File dst) throws IOException {
try (InputStream in = new FileInputStream(src)) {
try (OutputStream out = new FileOutputStream(dst)) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
}
}
To give a bit more clarity:
DB_NAME = "database.db"
For testing purposes, I have the "database.db" that I want to "import" in my downloads folder, later on I will convert this code so instead the device pulls the initial database from the server.
the dst.delete(); is just to delete the existing database file as I was testing things out (not sure if required)
This basically translates to: Start the app > Replace the database file > build the database.
Gotta be careful with the identity_hash in the room_master_table, if this is different from what's supposed to be it's not going to work.
Also, I pulled this idea from this answer:
https://stackoverflow.com/a/50383879/2150472
Hope it helps anyone!
P.S, if what you need is just to be able to see the data and perform CRUD operations you can always use this plugging: https://github.com/amitshekhariitbhu/Android-Debug-Database

How to use Room Persistence Library with pre-populated database?

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?
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.
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
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
Working 2019 solution without hacks or dependencies (Kotlin)
Place your .db file in assets/databases (or really any folder in there, as long as it's under assets).
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.
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.
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();
}
}
}
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()
you just copy assets/databases to app/databases
and than add addMigrations() in databaseBuilder
it will keep your data

Realm with pre populated data into assets?

Normally I use Realm as:
RealmConfiguration config = new RealmConfiguration.Builder(applicationContext).deleteRealmIfMigrationNeeded().build();
How can I add to the assets folder of my project a database with data and read it?
Since Realm Java 0.91.0 there has been an assetFile(String) option on the RealmConfiguration that automatically will copy a file from assets and use that if needed (e.g. if the Realm is opened the first time or has been deleted for some reason):
RealmConfiguration config = new RealmConfiguration.Builder()
.assetFile("path/to/file/in/assets") // e.g "default.realm" or "lib/data.realm"
.deleteRealmIfMigrationNeeded()
.build()
The above will copy the file from assets the first time the Realm is opened or if it has been deleted due to migrations (remember to update the asset Realm in that case).
OLD ANSWER:
It is possible to bundle a Realm database in the assets folder, but then you just need to copy it from there when starting the app the first time.
We have an example of how to copy the files here: https://github.com/realm/realm-java/blob/master/examples/migrationExample/src/main/java/io/realm/examples/realmmigrationexample/MigrationExampleActivity.java#L101-Lundefined
copyBundledRealmFile(this.getResources().openRawResource(R.raw.default_realm), "default.realm");
private String copyBundledRealmFile(InputStream inputStream, String outFileName) {
try {
File file = new File(this.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Since Realm 0.89.0 RealmConfiguration.initialData(Realm.Transaction) can now be used to populate a Realm file before it is used for the first time.
RealmConfiguration conf = new RealmConfiguration.Builder(context)
.initialData(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.beginTransaction();
realm.createObject(....)
realm.commitTransaction();
}
}).deleteRealmIfMigrationNeeded().name("mRealm.db").build();
Realm realm = Realm.getInstance(conf);
[EDIT] See Stan's answer below. Apparently Realm now supports this directly so you can ignore this answer (unless you're using older Realm versions).
We had a similar need, and also wanted support for a read-only realm database shared with an iOS version of the app.
We created a simple library and have open-sourced it. It includes the copy code given in #christian-melchior's answer, as well as some optional extra tracking for read-only realm database(s) bundled with the APK. Comments and PRs welcomed. See:
https://github.com/eggheadgames/android-realm-asset-helper
Realm has a special parameter in its RealmConfiguration.Builder called assetFile. You could use it like:
realmConfiguration = new RealmConfiguration.Builder()
.assetFile("dataBase/default.realm") // your app's packaged DB
...
.build();
just set yer assets DB path and file name and you are good to go without any android-realm-asset-helper lib or copy-file-from-assets code. In this example my app packaged DB-file lies in "assets/dataBase/default.realm".Note, version below 2 has a bit another way to call assetFile, you should pass context additionally:
realmConfiguration = new RealmConfiguration.Builder(context)
.assetFile(context, "dataBase/default.realm")
.build();
You can use assetFile() method. Please be aware that you can't use assetFile() with deleteIfMigrationNeeded().

Android: copy database from asset folder, but only get an empty file

guys, I have the problem when copying database from local assets folder to /data/data/package_name/databases directory. As I use the http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/ tutorial to do it, I can only get an empty file.
I quoted the part of copyDataBase() method and there is no difference. Every time the app start, it will create the directory and empty database. So is there any way to make the copyDataBase() work?
Thank you very much!!
Why wouldn't you copy from assets? It's perfectly normal to do so. But you can't do it in the onCreate, at that point an empty database is already created. You need to do it prior. I usually do it in an override of getWriteableDatabase, something like
public synchronized SQLiteDatabase getWritableDatabase() {
SQLiteDatabase db = null;
if (!doesDatabaseExist()) {
try {
copyDatabase();
db = super.getWritableDatabase();
} catch(Exception ex) {
Log.e("Database Log", getDatabasePath() + " failed to copy correctly. " + ex.getLocalizedMessage());
}
}
else {
db = super.getWritableDatabase();
}
return db;
}
I wouldn't copy any database form the assets-folder. If you need some standard entry's in your Database, you can add them using INSERTs in your onCreate()-method.
Update: Since this is getting down-voted for being wrong (which is kinda right) and I can't delete it, here is a little update.
I'd say it depends upon how many standard entries you want to add to your database. If it's just one or two, shipping a packed DB might not be worth it.
Anyways, some apps come with rather large databases (for example, a recipe collection). You can obviously not add all these in code.
For small test-entries, I'd still prefer simply adding them in onCreate().
For bigger databases, you should pre-populate them and ship em along with your app.
For the later to work, you'll need to copy the database file from assets/ to your app-folder. There is a nice library to handle that for you: android-sqlite-asset-helper
I don't know if it is still usefull but here is the solution for others that get here to see the awnser. The code you used, works for most phones, some older phones have different behaviour with the getReadableDatabase() function. Your problem therefore is not in the copyDataBase function but in the createDataBase function.
in createDataBase() there is the following check;
this.getReadableDatabase();
This checks if there is already a database with the provided name and if not creates an empty database such that it can be overwritten with the one in the assets folder. On newer devices this works flawlessly but there are some devices on which this doesn't work. Mainly older devices. I do not know exactly why, but it seems like the getReadableDatabase() function not only gets the database but also opens it. If you then copy the database from the assets folder over it, it still has the pointer to an empty database and you will get table does not exist errors.
So in order to make it work on all devices you should modify it to the following lines:
SQLiteDatabase db = this.getReadableDatabase();
if (db.isOpen()){
db.close();
}
Even if the database is opened in the check, it is closed thereafter and it will not give you any more trouble.
at the right above example worked for me this way:
db = super.getWritableDatabase();
db.close;
copyDatabase();
otherwise i got an IO error;
Here is the simple and convenient code which I use:
public class DataBaseImportHelper {
private DataBaseImportHelper() {}; // Avoid instantiation
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
public static boolean importDataBase(Context context) {
InputStream myInput = null;
OutputStream myOutput = null;
try {
// Open local db from assets as the input stream
myInput = context.getAssets().open(DATABASE_NAME);
createEmptyDatabase(context); // See this method below
// Open the empty db as the output stream
myOutput = new FileOutputStream(getDatabaseFile(context));
// transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myInput.close();
myOutput.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* Check if the database already exists.
* #return true if it exists, false if it doesn't
*/
public static boolean isDatabaseExists(Context context) {
return getDatabaseFile(context).exists();
}
private static File getDatabaseFile(Context context) {
return context.getDatabasePath(DatabaseHelper.DATABASE_NAME);
}
/**
* Create an empty database into the default application database
* folder.So we are gonna be able to overwrite that database with our database
*/
private static void createEmptyDatabase(Context context) {
// use anonimous helper to create empty database
new SQLiteOpenHelper(context, DatabaseHelper.DATABASE_NAME, null, 1) {
// Methods are empty. We don`t need to override them
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
#Override
public void onCreate(SQLiteDatabase db) {
}
}.getReadableDatabase().close();
}
}
And using in code:
if(!DataBaseImportHelper.isDatabaseExists(this)){
if (!DataBaseImportHelper.importDataBase(this)){
throw new IllegalStateException("Database doesn`t exist and hasn`t been copied!");
}
}

How to put existing database in the .apk file?

I have prebuild database of mostly string objects. I want to know how to put in my apk file so the database will be already created when the user installs the database.
I found a good example of this: Using your own SQLite database in Android applications
Basically, you export the created database as an sql-file and store it in the assets-folder.
On the first program start, you import the data of the file into your final database.
I think it's the best approach, however your data will be there twice in the apk and the db, using some more storage space.
I've just started developing for Android, and was surprised to discover that bundling a static database is not easy to do. So I did the only reasonable thing: created a library which does just that. Example usage:
import android.database.sqlite.SQLiteDatabase;
import kiwidrew.staticdb.StaticDatabase;
public class BlahBlah extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SQLiteDatabase db = StaticDatabase.openDatabase(this, "assets/foobar.db");
// Do normal database stuff with 'db'...
}
}
You get back a standard SQLiteDatabase object, with the only restriction being that it doesn't support writing. (Obviously!)
Note that this will fail unless the database is stored in your .apk without compression. Add the sqlite database using the aapt -0 command or modify your build.xml to pass the <nocompress extension="db" /> flag to the <aapt> tag...
Get the code at http://bitbucket.org/kiwidrew/android-staticdb.
Note: I've literally just finished writing this, and have only done very basic testing so far. Bug reports would be appreciated!
There are no any simple way to read database from assets directly. You should copy your database from assets to data folder in the first run, then when every time your app starts up, you should check database in the data folder and copy it again if the database does not exist.
These steps help you:
1) Execute these commands on your database, if android_metadata table does not exist in your database, android could not open your database.:
CREATE TABLE android_metadata(locale TEXT DEFAULT 'en_US')
INSERT INTO android_metadata VALUES('en_US')
2) Chunk your database because android does not support reading a file that more than 1 MB size from assets.
This python code chunks your database:
def chunk_file(file_name):
input_file = open(file_name, "rb")
chunk_counter = 0;
while True:
chunk = input_file.read(512 * 1024) # 512 KB
if chunk:
output_file_name = file_name + "." + str(chunk_counter).zfill(4)
output_file = open(output_file_name, "wb")
output_file.write(chunk)
output_file.close()
chunk_counter += 1
else:
break
input_file.close()
return
# Input: database.db
# Output: database.db.0000, database.db.0001, database.db.0002, ...
chunk_file("database.db")
Then put database.db.0000, database.db.0001, database.db.0002, ... in the assets folder.
3) Check database exists in the data folder when app starts up.
public static boolean databaseExists() {
boolean result = false;
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(
getApplicationContext().getFilesDir().getPath() + "/database.db",
null, SQLiteDatabase.OPEN_READONLY);
result = true;
} catch (SQLiteException exception) {
result = false;
}
if (checkDB != null) {
checkDB.close();
}
return result;
}
4) If database does not exist in data folder, copy database from assets to data folder.
public static void copyDatabase() throws IOException {
AssetManager assets = getApplicationContext().getAssets();
// database.db.0000, database.db.0001, database.db.0002, ... --> databaseChunks.
String[] databaseChunks = assets.list("");
Arrays.sort(databaseChunks);
OutputStream databaseStream = new FileOutputStream(
getApplicationContext().getFilesDir().getPath() + "/database.db");
for (int i = 0; i < databaseChunks.length; i++) {
String databaseChunkName = databaseChunks[i];
InputStream chunkStream = assets.open(databaseChunkName);
int length;
byte[] buffer = new byte[1024];
while ((length = chunkStream.read(buffer)) > 0) {
databaseStream.write(buffer, 0, length);
}
chunkStream.close();
}
databaseStream.close();
return;
}
5) You can connect to the database now:
SQLiteDatabase database = SQLiteDatabase.openDatabase(
getApplicationContext().getFilesDir().getPath() + "/database.db",
null, SQLiteDatabase.OPEN_READONLY);
// ...
database.close();

Categories

Resources