Androidで予め作っておいたSQLiteを使う

AndroidでデータベースといえばSQLiteとなると思う。

ただし、PC等でデータベースを作成しておいて、それをアプリから使うとなると少し大変。

プロジェクトのassetsフォルダに置いただけでは開くことができない。

 

そこで、起動時の始めの1回だけ、assetsに置いたデータベースファイルをデータベースパスへコピーしてやるという方法で解決する。

 

SQLite扱うクラスSQLiteDatabaseのインスタンスはSQLiteOpenHelperクラスを使って作るのが正しい方法なよう。

そこで、SQLiteOpenHelperを継承したクラスを作成。

getReadableDatabaseメソッド、getWritableDatabaseメソッドそれぞれで、データーベースパスのファイルの有無を確認して、なければassetsからコピーする。

 

ちなみに以下のソースでは、コンストラクタに渡すversionが変わった時も、コピーし直すようにもしている。

 

import java.io.IOException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;


public class SQLiteOpenFromAssets extends SQLiteOpenHelper {
        private Context mContext;
        private String  mDBName;
        private boolean mUpgrade;
        
        public SQLiteOpenFromAssets(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
                super(context, name, factory, version);
                mContext = context;
                mDBName  = name;
                mUpgrade = false;
        }
        
        protected void resourceCopyFromAsset() throws IOException {
                File         of;
                File         od;
                InputStream  is;
                OutputStream os;
                byte[]       buf;
                int          size;

                of  = mContext.getDatabasePath(mDBName);
                od  = new File(of.getParent());
                od.mkdirs();

                is  = mContext.getAssets().open(mDBName);
                os  = new FileOutputStream(of);
                buf = new byte[1024];
                for ( ; ; ) {
                        size = is.read(buf);
                        if (0 > size) break;
                        os.write(buf, 0, size);
                }
                os.flush();
                os.close();
                is.close();
        }
        
        @Override
        public SQLiteDatabase getReadableDatabase() {
                try {
                        SQLiteDatabase db;

                        if (! mContext.getDatabasePath(mDBName).exists()) {
                                resourceCopyFromAsset();
                        }
                        db = super.getReadableDatabase();
                        if (mUpgrade) {
                                close();
                                resourceCopyFromAsset();
                                db = super.getReadableDatabase();
                        }
                        return db;
                }
                catch (IOException e) {
                        return null;
                }
        }
        
        @Override
        public SQLiteDatabase getWritableDatabase() {
                try {
                        SQLiteDatabase db;

                        if (! mContext.getDatabasePath(mDBName).exists()) {
                                resourceCopyFromAsset();
                        }
                        db = super.getWritableDatabase();
                        if (mUpgrade) {
                                close();
                                resourceCopyFromAsset();
                                db = super.getWritableDatabase();
                        }
                        return db;
                }
                catch (IOException e) {
                        return null;
                }
        }
        
        @Override
        public void onCreate(SQLiteDatabase db) {
                mUpgrade = false;
        }

        @Override
        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                mUpgrade = true;
        }

        @Override  
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
                mUpgrade = true;
        }
}

使うときはこんな感じ

SQLiteOpenFromAssets helper = new SQLiteOpenFromAssets(context, "test.db", null, 1);
SQLiteDatabase db = helper.getReadableDatabase();


補足

SQLiteOpenHelperクラスのソースを見ればわかるが、super.getReadableDatabaseやsuper.getWritableDatabaseメソッド内でバージョンチェックが行われ、状況によってonDowngradeやonUpgradeメソッドが呼ばれる。

onUpgradeメソッドでコピー作業をしたいところだが、トランザクションの最中なので、ココではできない。なのでフラグだけ変えておいて後で処理を行う。