I wan't to use database file, that I've created on my computer.
While this is working, I'm considering that as a bad workaround, as it's not using existent API, while creating its own api.
I want to be able to use getWritableDataBase(), onCreate(SQLiteDatabase db) and onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) methods.
So, that's what I did and it's not working for some reason. I thought that if I would rewrite existent database it would work, but when querying I'm getting table not exists exception.
public void onCreate(SQLiteDatabase db) {
File f = new File(db.getPath());
try {
InputStream is = context.getResources().getAssets().open("words1.db");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(f);
fos.write(buffer);
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
So how could I use prepopulated database, while using existent api?
The easiest way to pre-package a database with your application is to use Jeff Gilfelt's SQLiteAssetHelper. While it does require you to add a small JAR to your project, his code is tested and debugged, and it requires very little additional code on your part.
Here is a sample project demonstrating the use of SQLiteAssetHelper.
Related
I have two databases, one database is the primary. This primary DB is responsible for holding the current data which is up to date and my secondary DB is populated via a cron job, once the primary DB gets obsolete I want to replace it with the secondary DB via a file operation of just over writing the existing DB and refreshing my views. Is it possible to do this, is there a better way?
So far what I have done is:
public void writeToSD() throws IOException {
File f=new File("/mnt/sdcard/dump.db");
FileInputStream fis=null;
FileOutputStream fos=null;
try{
fis=new FileInputStream(f);
fos=new FileOutputStream("/data/data/com.one.two/databases/Bdr");
while(true){
int i=fis.read();
if(i!=-1){
fos.write(i);
}
else{
break;
}
}
fos.flush();
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
fos.close();
fis.close();
}
catch(IOException ioe){
System.out.println(ioe);
}
}
How about always using the same database files (let's say dbA, dbB) with two instances of SQLiteOpenHelper and using an utility class like this instead of using raw SQLiteOpenHelper:
class Db {
private SQLiteOpenHelper mPrimaryDb;
private SQLiteOpenHelper mSecondaryDb;
public Db(Context context) {
mPrimaryDb = new MyDbHelper(context, "db_a");
mSecondaryDb = new MyDbHelper(context, "db_b");
}
public SQLiteOpenHelper getPrimaryDb() {
return mPrimaryDb;
}
public SQLiteOpenHelper getSecondaryDb() {
return mSecondaryDb;
}
public void swapDb() {
SQLiteOpenHelper tmp = mPrimaryDb;
mPrimaryDb = mSecondaryDb;
mSecondaryDb = tmp;
// TODO: notify data users that data has changed, cleanup the old primary database, etc.
}
{
If you want to use file operations, renaming the data base files is faster. But during file operations all connections have to be closed before any action.
If insertion is too slow, I would not overwrite the database file. I would generate the new database with a temp name and the same table and view structure. After finishing writing to the temp file I would rename the file to the same name as the invariant part of the old database plus a version number or a timestamp . And in my application I would look periodically for a new version, if found I would close all connections to the old file and open the new database.
I am on my 10+ hour of trying to get my sqlite database to export to sdcard as a backup and then import information back to my application. I have searched every topic I could to try and figure this out without having to ask any questions but now I have ran out of helpful info. I seem to be very very close to finishing this... Here's where I am: I am following this basic example http://androidgenuine.com/?tag=importexport-android-database . Everything seems to be working great and I can see the folder I created with my backup file and if I open the file as text I can see it is saving my information from my db. I then import my saved file back to my application and everything seems to go well also. The problem is the new information I load from my backup isn't displayed in my application, it still displays the same data as before the import. The weird thing is if I export my database after importing it the export file is the correct data that was supposed to be displayed, so I am apparently getting the correct data when I import it just doesn't show up and instead shows whatever old data I had before import. Again, it works however, just doesn't seem to refresh my data my application is reading. Thank you for any help with this, I am stumped.
Here is my import
myOutput = new FileOutputStream("/data/data/my_project/databases/empdb.db");
// Set the folder on the SDcard
File directory = new File("/sdcard/CarMaintenanceBackup");
// Set the input file stream up:
InputStream myInputs = new FileInputStream(directory.getPath()+ "/empdb.backup");
// Transfer bytes from the input file to the output file
byte[] buffer = new byte[1024];
int length;
while ((length = myInputs.read(buffer))>0)
{
myOutput.write(buffer, 0, length);
}
// Close and clear the streams
myOutput.flush();
myOutput.close();
myInputs.close();
} catch (FileNotFoundException e) {
Toast.makeText(mymenu.this, "Import Unsuccesfull!", Toast.LENGTH_LONG).show();
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) { Toast.makeText(mymenu.this, "Import Unsuccesfull!",
Toast.LENGTH_LONG).show();
// TODO Auto-generated catch block
e.printStackTrace();
}
Toast.makeText(mymenu.this, "Import Done Succesfully!", Toast.LENGTH_LONG).show();
edit*
my OpenHelper
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public OpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CARINFO_TABLE);
db.execSQL(HISTORY_TABLE);
db.execSQL(MPG_TABLE);
db.execSQL(MODS_TABLE);
db.execSQL(PICK_TABLE);
//UPDATE//
db.execSQL(VEHICLENAME_TABLE);
db.execSQL(ODOMETER_TABLE);
db.execSQL(COUNTER_TABLE);
db.execSQL(SETTIME5_TABLE);
db.execSQL(USORMETRIC_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
// TODO Auto-generated method stub
if (arg2 > arg1) {
db.execSQL(PICK_TABLE);
db.execSQL("ALTER TABLE Employees ADD COLUMN _datetask INTEGER DEFAULT 0");
}
}
}
I am currently testing the Android API, and to do so, I have developed an application that copies a database that I have made beforehand and testing that application (which calls on several Android classes). Someone directed me to using Robolectric to get coverage of the Android source, however, this has caused some problems. My application runs just fine without it, as well as my test, but now I'm running into errors with the copying of my database. Whenever I run the test, my catch gives me an error from copying the database, and taking out that catch results in an AssertionFailureError
assertTrue(activity.accessAdapter() != null);
assertTrue(activity.accessAdapter().accessHelper().checkDatabase()); // Here
I'm assuming that has something to do with not getting the database copied, as if I put back in the try-catches I had in my code, it results back in setting them off when copying the database.
Here's the code for my SQLiteOpenHelper, or at least what gets called up in copying the database.
public void createDatabase() throws IOException
{
this.getReadableDatabase();
this.close();
try {
copyDatabase();
Log.e(TAG, "Database created.");
} catch (IOException e) {
throw new Error("Error copying database.");
}
}
private void copyDatabase() throws IOException {
InputStream input = ((ShadowContextWrapper) shadowContext).getAssets().open(DATABASE_NAME);
String outFileName = DATABASE_PATH + DATABASE_NAME;
OutputStream output = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) > 0) output.write(buffer, 0, length);
output.flush();
output.close();
input.close();
}
I assumed the problem might have to do with the context, so I created a ShadowContext, but that didn't really help either. There are other errors, but those are just methods that call each other up all the way down to the createDatabase() in my SQLiteOpenHelper.
Does anyone know how I can copy this database using Robolectric? I have it saved in assets for my application, but obviously, without the emulator, this is pointless. Thank you.
P.S. I can add any more code if necessary, this is just what I thought was necessary at first.
I wish to pack a lot of data in my android package. May I use database for this? If yes, then how to pack database tables with APK?
You may put your database in the assets/ folder and when your application is run for the first time use the following code to copy your databases where they are supposed to be:
private void copyFromAssets() {
InputStream istream = null;
OutputStream ostream = null;
try {
istream = context.getAssets().open(DATABASE_NAME);
File path = context.getDatabasePath(DATABASE_NAME);
if (path.exists() == false)
path.createNewFile();
ostream = new FileOutputStream(path);
byte[] buffer = new byte[8192];
int length;
while ((length = istream.read(buffer))>0) {
ostream.write(buffer, 0, length);
}
ostream.flush();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Failed to copy database: " + DATABASE_NAME);
} finally {
try {
if (ostream != null) ostream.close();
if (istream != null) istream.close();
} catch (IOException e) {}
}
}
After that you may use your database the usual way.
To create the table you can use the SQLiteOpenHelper (ref here and credit there)
private class MyOpenHelper extends SQLiteOpenHelper {
public MyOpenHelper(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
#Override public void onCreate(SQLiteDatabase db)
{
// Replace this SQL code with the code for your database.
String query = "CREATE TABLE people (" +
"_id integer primary key autoincrement not null, " +
"first_name text, last_name text);";
db.execSQL(query);
}
#Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
// Called when the database needs to be upgraded. The implementation
// should use this method to drop tables, add tables, or do anything
// else it needs to upgrade to the new schema version.
}
}
The table will be created or updated when accessed if needed.
You can also populate the database if needed after table creation.
You might want to provide a web-service to feed the initial data if it can help decreasing the size of the APK (and if size is your concern).
Since APKs are still limited to 50MB you could try adding the database as an expansion file, separately from the application. Here are more details: http://android-developers.blogspot.com/2012/03/android-apps-break-50mb-barrier.html
I use the following code to add rows to my database :
public void insert(String kern, String woord) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KERN, kern);
values.put(WOORD, woord);
db.insertOrThrow(TABLE_NAME, null, values);
return;
Currently, I'm invoking this insert() 3.455 times, to add all words to the database, using : insert("Fruits", "Banana"); It takes forever.
How can I change this code to work faster? I'm thinking in the line of foreach, but don't know how to implement.. Thanks!
/Edit; The solution provided by #hovanessyan works and will do the job. AND.. note that if you have a lot of lines that have to be put in, you might be confronted with the method exceeds max byte limit error. In that case, review the other solution, that suggests packing the database in the actual .APK file.
You can wrap-up those inserts into transaction.
db.beginTransaction();
try {
// do all the inserts here
//method call here, that does 1 insert; For example
addOneEntry(kern,woord);
...
db.setTransactionSuccessful();
} catch (SQLException e) {
//catch exceptions
} finally {
db.endTransaction();
}
private void addOneEntry(String kern, String woord) {
//prepare ContentValues
//do Insert
}
You can use bulkInsert:
ContentValues[] cvArr = new ContentValues[rows.size()];
int i = 0;
for (MyObject row : rows) {
ContentValues values = new ContentValues();
values.put(KERN, myObject.getKern());
values.put(WOORD, myObject.getWoord);
cvArr[i++] = values;
}// end for
resolver.bulkInsert(Tasks.CONTENT_URI, cvArr);
Using the tips of both hovanessyan and Damian (remind me to rep+1 you as soon as I reach 15 ;), I came up with the following solution:
For relatively small databases (<1,5Mb)
I created the database using SQLite Database Browser, and put it in my Assets folder.
Then, the following code copies the database to the device, if it's not already there:
boolean initialiseDatabase = (new File(DB_DESTINATION)).exists();
public void copyDB() throws IOException{
final String DB_DESTINATION = "/data/data/happyworx.nl.Flitswoorden/databases/WoordData.db";
// Check if the database exists before copying
Log.d("Database exist", "" + initialiseDatabase);
Log.d("Base Context", "" + getBaseContext());
if (initialiseDatabase == false) {
// Open the .db file in your assets directory
InputStream is = getBaseContext().getAssets().open("WoordData.db");
// Copy the database into the destination
OutputStream os = new FileOutputStream(DB_DESTINATION);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0){
os.write(buffer, 0, length);
}
os.flush();
os.close();
is.close();
}}
In my app, a portion of the database is User-customizable.
I call the code above in onStart() with :
try {
copyDB();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
So, when the user presses "reset database to standard" (in preferences screen), I just set the Boolean initialiseDatabase to "false" and wait for the user to go back to the main activity. (thus calling onstart and copying the original database).
I tried to call the Activity.copyDB() from the preferences.java. It's neater, because it doesn't require the user to go back to the main activity to rebuild the database. However, I get an error about not being able to call static references to non-static methods. I don't understand that, but will look into it.