Wednesday, March 9, 2011

Manage Sqlite Database Size in Greater than 1MB in Android

How To Manage SQLite Databases With Size More Than 1MB In Android?


Adding a database larger than 1MB into apk file is bit tricky. You might have already gone through many blogs and forums to find answer for this and could have found below answers:


1. Rename the DB file to add extensions like JPG,PNG,MP3 which are already compressed and then copy the database to /data/data//databases.
       Shortcomings of this method are - 
          a. Your APK size will be large. People will Hesitate to download your app.
          b. It will consume your precious internal memory.
2. Do not bundle your database file with the apk file and instead of that download the database file from your server.
  Shortcomings of this methods are- 
        a. Users likes to use their applications as soon as they install app.
        b. You would have decided to use the built in DB because you do not have / do not want a webserver to support this app.


3. Split your database into chunks less than 1MB
        Shortcomings of this method are - 
            a. This will in no way reduces the APK size
            b. You would have to write a logic to merge all the file.


Finally I was able to find a solution for this and was able to overcome all the above short comings.  
4. Zip and Unzip Method: In this method we will zip the database file before building APK and we will Unzip the database into SD card when the app. is run for the first time. Follow the below steps to achieve this.


Steps-

  1. zip the sqlite database file using winzip or 7z and place it in /res/raw.
  2. In your Oncreate method, Check to see if it is the first time the application launched.
  3. Unzip the database file and place it in the SD card 
  4. Connect to the database from the SD card

Code for Obtaining the external cache directory
/**
     * Creates a empty database on the system and rewrites it with your own database.
     * */
    public void createDataBase() throws IOException{

        //this will Returns the absolute path to the directory on the external filesystem (SD CARD) where the application can place cache files it owns. The files in this directory will be deleted when the application is uninstalled. The path will be like - /sdcard/Android/data/[packagename]/cache/
       File DB_PATH = myContext.getExternalCacheDir();
     DB_PATH.mkdirs();
     File db = new File(DB_PATH, DB_NAME);
     if(!db.exists()) {
      db.createNewFile();
      try {
          
       //copyDataBase();
          copyFromZipFile();

      } catch (IOException e) {
               
          throw new Error("Error copying database",e);

         }
     }
Code for Unzipping database file - 
private void copyFromZipFile() throws IOException{
 InputStream is = myContext.getResources().openRawResource(R.raw.materia_medica);
     // Path to the just created empty db
     File outFile = new File(DB_PATH ,DB_NAME);
      //Open the empty db as the output stream
     OutputStream myOutput = new FileOutputStream(outFile.getAbsolutePath());
     ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
      try {
          ZipEntry ze;
          while ((ze = zis.getNextEntry()) != null) {
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              byte[] buffer = new byte[1024];
              int count;
              while ((count = zis.read(buffer)) != -1) {
                  baos.write(buffer, 0, count);
                  //Log.d("", buffer.toString());
              }
              baos.writeTo(myOutput);
              

          }
      } finally {
          zis.close();
          myOutput.flush();
          myOutput.close();
          is.close();
      }
    }

Code to connect to the Database on SD card - 
public SQLiteDatabase openDataBase() throws SQLException{
 
     File DB_PATH = myContext.getExternalCacheDir();
        File dbFile = new File (DB_PATH,DB_NAME);
     myDataBase = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
     Log.d("database path:",myDataBase.getPath());
     
     return myDataBase;
    }

Advantages of the 4th method:

  1. Reduced APK size. Sqlite database can be compressed by 75% using winzip or 7z.
  2. Database is stored on the external SD card.
  3. No noticeable performance change by reading database from external SD card.
  4. Android install manager will take care of deleting the data when the application is uninstalled.

Possible Shortcomings:

  1. User should have external SD card Installed.
  2. The application will not be able to access the data when SD card is being mounted to a PC. In fact you cannot launch the application itself if it is installed on SD card.

8 comments:

Anonymous said...

Nice blog.. Thanks a lot.
I have a database of 40MB.
When i try to do an inputstream.Read() with it, it fails misrebly.
I am sure its the issue with the size.
Now, I zipped it into 7mb using WinZip as you suggested. And i used your code it fails at ze = zis.getNextEntry(), it returns null at the first loop.
Can you give a suggestion on how to go about this. Do you think this is the issue with the size.
Any help would be greatly appreciated.
Thanks in Advance. Keep blogging.

Anonymous said...

Awesome code, but it will fail with an out of memory message with larger databases (I was using 17mb unpacked)

Use a BufferedOutputStream instead of a ByteArrayOutputStream to get around this problem.

Anonymous said...

Thanks buddy. BufferedOutputStream did work for me too.

Anonymous said...

We can user FileOutputStream also.

Anonymous said...

I'm having a similar problem as the first poster. The line "ze = zis.getNextEntry())" is throwing an IO exception and I can't figure out why.

Hardik Joshi said...
This comment has been removed by the author.
Anonymous said...

How to Call this methods in my onCreate() method.. please give me full example.. Thank you.

Devendra Chouksey said...

Exception "no such table found while compling " when i query.What to do.