Stream: Anybody play with the new Xcode 4 preview 2 yet? http://bit.ly/b286GD Jobs: We're Hiring! >>
ReignDesign

Blog - Using your own SQLite database in Android applications

Using your own SQLite database in Android applications

Posted March 3rd, 2009 by Juan-Manuel Fluxà

Android Dev.Most all of the Android examples and tutorials out there assume you want to create and populate your database at runtime and not to use and access an independent, preloaded database with your Android application.

The method I'm going to show you takes your own SQLite database file from the "assets" folder and copies into the system database path of your application so the SQLiteDatabase API can open and access it normally.

1. Preparing the SQLite database file.

Assuming you already have your sqlite database created, we need to do some modifications to it.
If you don't have a sqlite manager I recommend you to download the opensource SQLite Database Browser available for Win/Linux/Mac.

Open your database and add a new table called "android_metadata", you can execute the following SQL statement to do it:

CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US')

Now insert a single row with the text 'en_US' in the "android_metadata" table:

INSERT INTO "android_metadata" VALUES ('en_US')

Then, it is necessary to rename the primary id field of your tables to "_id" so Android will know where to bind the id field of your tables.
You can easily do this with SQLite Database Browser by pressing the edit table button Edit Table, then selecting the table you want to edit and finally selecting the field you want to rename.

After renaming the id field of all your data tables to "_id" and adding the "android_metadata" table, your database it's ready to be used in your Android application.

Modified database

Modified database

Note: in this image we see the tables "Categories" and "Content" with the id field renamed to "_id" and the just added table "android_metadata".

2. Copying, opening and accessing your database in your Android application.

Now just put your database file in the "assets" folder of your project and create a Database Helper class by extending the SQLiteOpenHelper class from the "android.database.sqlite" package.

Make your DataBaseHelper class look like this:

public class DataBaseHelper extends SQLiteOpenHelper{
 
    //The Android's default system path of your application database.
    private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/";
 
    private static String DB_NAME = "myDBName";
 
    private SQLiteDatabase myDataBase; 
 
    private final Context myContext;
 
    /**
     * Constructor
     * Takes and keeps a reference of the passed context in order to access to the application assets and resources.
     * @param context
     */
    public DataBaseHelper(Context context) {
 
    	super(context, DB_NAME, null, 1);
        this.myContext = context;
    }	
 
  /**
     * Creates a empty database on the system and rewrites it with your own database.
     * */
    public void createDataBase() throws IOException{
 
    	boolean dbExist = checkDataBase();
 
    	if(dbExist){
    		//do nothing - database already exist
    	}else{
 
    		//By calling this method and empty database will be created into the default system path
               //of your application so we are gonna be able to overwrite that database with our database.
        	this.getReadableDatabase();
 
        	try {
 
    			copyDataBase();
 
    		} catch (IOException e) {
 
        		throw new Error("Error copying database");
 
        	}
    	}
 
    }
 
    /**
     * Check if the database already exist to avoid re-copying the file each time you open the application.
     * @return true if it exists, false if it doesn't
     */
    private boolean checkDataBase(){
 
    	SQLiteDatabase checkDB = null;
 
    	try{
    		String myPath = DB_PATH + DB_NAME;
    		checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
 
    	}catch(SQLiteException e){
 
    		//database does't exist yet.
 
    	}
 
    	if(checkDB != null){
 
    		checkDB.close();
 
    	}
 
    	return checkDB != null ? true : false;
    }
 
    /**
     * Copies your database from your local assets-folder to the just created empty database in the
     * system folder, from where it can be accessed and handled.
     * This is done by transfering bytestream.
     * */
    private void copyDataBase() throws IOException{
 
    	//Open your local db as the input stream
    	InputStream myInput = myContext.getAssets().open(DB_NAME);
 
    	// Path to the just created empty db
    	String outFileName = DB_PATH + DB_NAME;
 
    	//Open the empty db as the output stream
    	OutputStream myOutput = new FileOutputStream(outFileName);
 
    	//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();
    	myOutput.close();
    	myInput.close();
 
    }
 
    public void openDataBase() throws SQLException{
 
    	//Open the database
        String myPath = DB_PATH + DB_NAME;
    	myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
 
    }
 
    @Override
	public synchronized void close() {
 
    	    if(myDataBase != null)
    		    myDataBase.close();
 
    	    super.close();
 
	}
 
	@Override
	public void onCreate(SQLiteDatabase db) {
 
	}
 
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 
	}
 
        // Add your public helper methods to access and get content from the database.
       // You could return cursors by doing "return myDataBase.query(....)" so it'd be easy
       // to you to create adapters for your views.
 
}

That's it.
Now you can create a new instance of this DataBaseHelper class and call the createDataBase() and openDataBase() methods. Remember to change the "YOUR_PACKAGE" to your application package namespace (i.e: com.examplename.myapp) in the DB_PATH string.

       ...
 
        DataBaseHelper myDbHelper = new DataBaseHelper();
        myDbHelper = new DataBaseHelper(this);
 
        try {
 
        	myDbHelper.createDataBase();
 
 	} catch (IOException ioe) {
 
 		throw new Error("Unable to create database");
 
 	}
 
 	try {
 
 		myDbHelper.openDataBase();
 
 	}catch(SQLException sqle){
 
 		throw sqle;
 
 	}
 
        ...


Enjoyed this post? ReignDesign is a great team of tech-savvy developers providing RIA and mobile services. For more articles like this, subscribe to our blog feed.

Tags: , , , ,

111 Responses to “Using your own SQLite database in Android applications”

  1. todd says:

    Solid writeup. We should take a look at modifying a lightweight ORM to work on Android. Something similar to iBATIS (http://ibatis.apache.org) with special hooks to setup and upgrade the database.

  2. Will says:

    Wow, much better then reading and executing 7000+ inserts. Brought an 18 second operation down to about 2 seconds.

    Please note that on Firefox 3.0.7 on Ubuntu 8.10 the greater than sign in the while loop shows up as its html code (& g t ;)

  3. Well presented. But not a good solution for large databases (fine for small ones).

    This DOUBLES the footprint of your database. A better solution for any sizeable database, is to download the database when the app is first run, as a secondary installation.

    This, at least, only eats up X precious megabytes once.

    Still suboptimal is the fact that the database must reside in /data/data/YOUR_PACKAGE/databases – because anything bulky should be on the sd card. I guess google will eventually let us access databases in other locations, or install our apps directly on the SD card.

  4. Justin Jaynes says:

    There is an HTML error in the code preventing a Greater Than sign from appearing and the symbols %gt; instead. It is in the while loop of the copyDataBase method.

  5. fluxa says:

    HTML error in the code fixed, thanks for the feedback.

  6. David says:

    This is exactly what I have been looking for.. But I am having an issue. I keep getting a failed to open the database errors. “sqlite3_open_v2(“/data/data/com.testapp/databases/database.db”, &handle, 1, NULL) failed”. I have tried this on both .db and .db3 files and neither of them are working. I followed you tutorial and placed the db in the assets directory, both in a folder called databases and just in the directory. Is there something else I am doing wrong?

  7. David says:

    I just realized the issue is not the loading of the database it is actually the first time it is read. Your tutorial is working properly thanks.

  8. Will says:

    I came across one issue, based on the lack of complaints I think it’s fairly unique. The first SELECT I ran on a table in the copied DB crashed with Android claiming the table did not exist. I checked through adb and sqlite3 and the table *did* exist. Eventually I tried CREATEing the table just before I SELECTed from it (since it allegedly didn’t exist). Android’s response was that the database file was corrupt. The solution was to programmatically open the database like normal, immediatly close it, than open it again. Prior to that I did try copying a completely closed database to /assets; the first SELECT still crashed.

    Here’s the first error for searchability:
    03-12 01:17:22.810: ERROR/AndroidRuntime(512): Caused by: android.database.sqlite.SQLiteException: no such table: tblMyTable: , while compiling: SELECT …

  9. Zek says:

    To Will :

    Try to add :
    mOpenHelper.close();

    Just before – SQLiteDatabase db = mOpenHelper.getReadableDatabase(); – in query function of your provider.

    That’s solve the same problem for me.

  10. George F. says:

    For me I get an ioexception thrown on the first
    read statement:

    while ((length = myInput.read(buffer))>0){

    Everything seems to get opened up just fine. I’ve named
    the db ls.db in the assets folder. Should it be in the assets
    folder per se or under some subdir? Any suggestions on how
    to debug the failed read?

    Thanks much

  11. fluxa says:

    George,

    Better if you get rid of the extension of your database file ( just “ls” ).

    Your database file in the assets folder is right, then:

    InputStream myInput = myContext.getAssets().open(“ls”);

    it should open your database file, put a break point after this line to see if it’s working.

    Also be sure that the path to your system application folder is right.

    /data/data/your.package.name/databases/

    then your output path should be

    /data/data/your.package.name/databases/ls

    good luck

  12. Hamy says:

    Thanks for being the first post I have been able to find using multiple tables!

    If you have time for another writeup, or an addition, it would be really useful to see how you are grabbing the data from a multi-table database. Using Cursors is obviously much harder than it is on one table, and I have not really found any help on a recommended way to do that.

    Thanks again!
    Hamy

  13. Wilson L. says:

    Excelent post! Congratz!

    George F., I was wondering if you have solved your problem reading the assets/your_db_name. I’m facing the same problem now, and I don’t know what is going on, because I can read it in terminal with sqlite command. Also I’m proceeding exactly as fluxa said.

    Thanks in advance!

  14. fluxa says:

    Wilson:

    Check you database file size, there is a limitation around 1.2 Mb for files in the asset folder.
    Wilson splitted up his db using Unix split command, added them in the res/raw folder, and then opened them up at install time to read. Then proceeded to splice them back together into the db and all worked fine.
    Thanks George for this.

  15. Wilson L. says:

    Thank you very much fluxa!

    It was indeed the file size.. I was able to redesign the database, actually the problem was helpful somehow, because I cleaned the tables (the overall schema).

    I’m dealing with a large amount of data, at first I was going to set up a web service to retrieve the info online, but my tutor didn’t like the idea of using internet… so… Ok, let’s try to downoad a little piece of internet and store it in a sqlite database. Seems absurd (and it is), but he was very reluctant :/

    Thank you again and Thanks George for a clever solution!

  16. BGH says:

    Thanks for the tut, this is exactly what I need. I’m having a problem though

    When it starts it recognises that there is no db and creates the empty db fine. Then in the copyDataBase method when it gets the the line

    //Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(outFileName);

    I get the error

    06-30 00:01:35.901: INFO/WTF(29581): java.io.FileNotFoundException: /data/data/bgh.com.spanishflashcards/databases/spanish

    The path is correct. It has to be because it used the same path when creating the blank db. The blank db definitely exists. If I pause the code before this step I can see the blank db in DDMS.

    Any reason why I would get that error?

  17. Rams says:

    THanks For the tutorial…

    am also getting the same error like BGH says…have any reason…

    BGH:Did u find the solution?

  18. Tzur says:

    Hi,

    thanks for addressing this important issue. Many apps need this flow.
    one question,
    what if my table just doesn’t have a column name “id”, what happens in this case?
    here’s a sample schema of my table,
    .schema OPR_CHEMISTRY_TAB
    CREATE TABLE OPR_CHEMISTRY_TAB (ENTRY_ID text, INDEX_LETTER text, INDEX_ORD int, ENAME text, DESCR text, HAS_IMAGES int);
    CREATE INDEX ENTRY_ENTRY_ID_IX on OPR_CHEMISTRY_TAB(ENTRY_ID asc);
    CREATE INDEX ENTRY_ENTRY_LETTER_ORD_IX on OPR_CHEMISTRY_TAB(INDEX_LETTER asc, INDEX_ORD asc);

    what does the native android table structure expects in this case?
    thanx!
    –tzurs

  19. dennie says:

    Hi, I just want to know how can I delete the table after I created it?

  20. Jimmy says:

    Is the android_metadata table necessary?

  21. fluxa says:

    Hi Jimmy, yes it is.

  22. p6majo says:

    Hi,

    would it be possible to have a database that is kept on the sdcard?
    I would like to have access to my database with other applications as well and I cannot access it, when it is stored in the /data/data/… directories. I’m not a superuser on my phone and I’m a little bit scared of loosing all my data when doing this goldcard business to become a su.

    Therefore, I’m looking for a possibility of using the sql database in my applications but storing it’s data on the sdcard.
    Will it be possible?

    Johannes.

  23. minhbu says:

    Thanks for the tut, this is exactly what I need. But I having a problem when I try to store file Image. I try to use some sqlite manager but not work. Please! Help me to solve this problem.
    Anyway, thanks a lot.

  24. Serg Podtynnyi says:

    I had problems with this code, it was not properly copied file into internal storage.
    A added
    this.close();
    before copyDataBase();
    and it works like charm.

  25. Daniel Lew says:

    @BGH:
    This is a little late now, but I had the same problem, but I figured out the problem.

    The issue comes up when this db is the FIRST db you try to create. If that’s the case, then data/data/YOURPACKAGE/databases doesn’t exist yet. You have to create the /databases directory or the method will fail. It’s pretty easy though:

    File f = new File(DB_PATH);
    if (!f.exists()) {
    f.mkdir();
    }

  26. cousinHub says:

    There is also a table called “sqlite_sequence”
    in a Android SQLite DB…

    This table has 2 columns : name and seq.

    Do we need to take care of it ?

    If I open the DB with:

    public void openDataBase() throws SQLException{

    String myPath = DB_PATH + DB_NAME;
    myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);
    }

    instead of using (OPEN_READONLY):

    myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    to be able to add records in the DB.

    I then noticed that “seq” within the table “sqlite_sequence” is not changing,
    even as I add or delete records successfully from the DB ???

    I viewed this by doing a “pull a file from the device” within the DDMS view /data/data/PACKAGE_NAME/databases/
    (and then opening this file with SQLite Browser.

    Is that normal/ ok ?

    _id is set to INTEGER PRIMARY KEY (works fine to add records)
    but should I also take care of “seq” an increase it by 1,
    everytime a record is added to the DB ???

    Txs for any advice on this.

  27. deep says:

    hi,
    I have more then 1.5 MB of DB so can anybody tell me how to connect the DB with the android because i cant put my DB in assests folder(Assets can take only upto 1.2 MB of DB).

  28. David Weaver says:

    Hello,

    The database apparently loads perfectly but when I come to try and run a query I get the message:

    11-17 20:54:03.126: ERROR/AndroidRuntime(749): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.allergycookbook/com.allergycookbook.AllergyCookBook}: android.database.sqlite.SQLiteException: no such table: recipes: , while compiling: SELECT _id, recipe_name FROM recipes

    I double checked and the table exists in the database (as does the field). I think it might be the same problem that Will posted above, but I tried to close and then open the database and it didn’t work.

    Perhaps it’s my shaky knowledge of SQL to blame? The code I am using to run the query was cribbed from the Android notepad tutorial and then modified accordingly. See below:

    private void fillData()
    {
    // Get all of the rows from the database and create the item list
    mNotesCursor = myDbHelper.fetchAllNotes();
    startManagingCursor(mNotesCursor);

    // Create an array to specify the fields we want to display in the list (only TITLE)
    String[] from = new String[]{DataBaseHelper.KEY_TITLE};

    // and an array of the fields we want to bind those fields to (in this case just text1)
    int[] to = new int[]{R.id.text1};

    // Now create a simple cursor adapter and set it to display
    SimpleCursorAdapter notes =
    new SimpleCursorAdapter(this, R.layout.recipe_row, mNotesCursor, from, to);
    setListAdapter(notes);
    }

    If anyone can help me I’d be most grateful.

  29. Alocaly says:

    Hi Deep,

    Actually I had this issue too. The anwser is simple : cut your big database file as several sub 1 Mo files !
    I gave some explanations on this subject here :
    http://androidblogger.blogspot.com/2009/05/how-to-ship-application-with-pre-baked.html

    Hope it helps !

  30. Deep says:

    Hi Alocaly,

    Thanks for your help but the issue is that my DB is in SQLite manager so how to cut SQLite DB so that we can able to connect it to our Android application. Please help me out of this problem.

    Thanks
    Deep

  31. Pieter Bonne says:

    Thank you for this article! I succesfully use an embedded an sqlite database in my application, but at first I had many issues with android not wanting to open the database. I tried using most of the available opensource sqlite editors but they all seemed to produce incompatible sqlite files. In the end I ended up building up my database programmatically using a jdbc sqlite driver (http://www.zentus.com/sqlitejdbc) which seems to produces files android has no problem with!

  32. Poson says:

    The DB is not found… I have met the same problem too.Mybe we should think about changing permissions of the DB file. But how to do it in code?

    Please help me out of the problem.

  33. Alocaly says:

    For Deep :
    In my blog article, I explained how I cut my database, and remerge it at the first launch of the game !
    http://androidblogger.blogspot.com/2009/05/how-to-ship-application-with-pre-baked.html

  34. Michael says:

    Hi Everyone,

    I have the same error in the logcat as David… “sqlite3_open_v2(”/data/data/com.testapp/databases/database.db”, &handle, 1, NULL) failed”.. I have the method running on the initial activity that runs in my app and it force closes. I close, then open the app up and no error. I can’t figure out why I’m crashing only on the initial run…

    Any help would be great!

  35. rabbit says:

    Hi everyone,

    i have follow the article, but i got some error message shows : java.lang.IlleagalStateException: database not open
    but i did open the database before i access it.
    anyone can help me ???
    thanks .

  36. jim says:

    I found a simplier way.
    Just copy a good one and start with that.
    Details:

    1. Run the example called Events1. Its a bunch of example code (my Bible) from The Pragmatic Bookshelf.
    2. From eclipse->DDMS, get the file in data/data/Events/databases/events.db
    3. eclipse->DDMS->top corner, Click on “Pull a file from the device”, put it on your windows desktop, then click on “Push a file into the device”.

  37. Mark Sherman says:

    Thanks for this post. It really helped us with our project.

    Mark

  38. vantan says:

    Hi all !
    I have got a file /sdcard/data.db .How to insert record into file data.db

  39. In order to waste less space, you should first compress your database file.
    Then just reaplce:

    //Open your local db as the input stream
    InputStream myInput = myContext.getAssets().open(DB_NAME);

    with

    //Open your local db as the input stream
    GZIPInputStream myInput = new GZIPInputStream( myContext.getAssets().open(DB_NAME) );

    for gzip or if you use zip

    //Open your local db as the input stream
    ZipInputStream myInput = new ZipInputStream( myContext.getAssets().open(DB_NAME) );
    myInput.getNextEntry();

  40. Joakim Lodén says:

    My previous post on compressing the database to save storage space will not do much, since the apk is compressed anyway.
    But you can still use this method as a convenient method to shrink the file under the 1MB limit (or you can just split the file in 1MB chunks and put it together when copying).

    Cheers!

  41. Achie says:

    Hello,

    It is a nice and a very helpful tutorial. Thank you.

    I have seen a couple of replies here which say that we can split large database files and then join them on the device. I need to implement it since my database size after compression is around 3MB.

    Can some one also let me know how to split and join large files?
    Thank you.

  42. darkdusky says:

    Hi, I’ve followed the tutorial but cannot get it working. It crashes in the method copyDataBase(), on the line:
    OutputStream myOutput = new FileOutputStream(outFileName);

    I tried the 2 suggestions of checking directory exists (if (!f.exists()) {f.mkdir();}) and this.close();. But neither worked. How do I check the error logs in emulator? This is my first Android app, so sorry for basic question.

  43. darkdusky says:

    RE: size of db file – I thought this link might be useful:
    http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

    If the db file contains any fragmentation the easiest way is to copy its contents to a new file (data is copied into a new unfragmented db)
    (DOS / Win prompt)
    > echo .dump | sqlite file1.db > file1.sql
    > sqlite file2.db sqlite file1.db .dump > file1.sql
    > sqlite file2.db < file1.sql

  44. darkdusky says:

    Found how to read log “adb logcat >C:\temp\log.txt”
    The error during copydatabase is:
    W/System.err( 704): Can’t dispatch DDM chunk 4d505251: no handler defined

    E/Database( 704): sqlite3_open_v2(“/data/data/MyApp/databases/main”, &handle, 1, NULL) failed

    Using Google I found 4d505251 is hex for MPRQ, or Method PRofiling Query. But still have no solution.

  45. Mike Stubber says:

    Hi, thank you very much for this great tutorial!

  46. sephy says:

    Thx for the tuto, but is there another way than with adb shell to see the folder containing the database with the emulator?
    Because, I can’t find it on disk…and to use SQLite browser i need to specifit the place of the folder…
    Help plz

  47. Dman says:

    I have some Newb questions:

    The second half of the tutorial says declare the new helper like this:

    DataBaseHelper myDbHelper = new DataBaseHelper();

    However the error I get is that there is no constructor that takes no arguments.

    So then I create my object all in one line:

    DataBaseHelper myDbHelper = new DataBaseHelper(this);

    Then I try to invoke this function:

    myDbHelper.createDataBase();

    And it says:

    Syntax error on token “createDataBase”, Identifier expected after this token

    What am I doing wrong?

    Thanks!
    Dman

  48. Mikey says:

    Just so that you know, god kills a kitten every time you write code such as this:

    return checkDB != null ? true : false;

    All you actually need to write is this:

    return checkDB != null;

  49. Barhoumi says:

    Hi!!
    i’ve followed all the stapes you’ve indicated
    i’ve created a database baseSQLite.db contain’ 3 tables and the meta one
    i’ve COPIED the code of dataHELPER in a class called BDAccess
    i’ve created an activity containin’ a textViw label that will indicate the establishement of connexion to the database or the error msg
    i’ve placed the database file into the asset folder
    changed the name of DB_NAME (without puttin the extension .db”
    in the activity i’ve created an instance of the BDClass , in one line cause i’ve not a constructor that accept no arguments
    but i got an error while launchin, the app is not respondin’ and i’ve got to force it to close

    i’ve checked the /DATA/DATA…. it contains the database but i don’t know if it’s empty (have you an idea how to check this)???

    what shall i do
    and THANKS!!!!!!

Leave a Reply