基于Android如何实现将数据库保存到SD卡
有时候为了需要,会将数据库保存到外部存储或者sd卡中(对于这种情况可以通过加密数据来避免数据被破解),比如一个应用支持多个数据,每个数据都需要有一个对应的数据库,并且数据库中的信息量特别大时,这显然更应该将数据库保存在外部存储或者sd卡中,因为ram的大小是有限的;其次在写某些测试程序时将数据库保存在sd卡更方便查看数据库中的内容。
android通过sqliteopenhelper创建数据库时默认是将数据库保存在'/data/data/应用程序名/databases'目录下的,只需要在继承sqliteopenhelper类的构造函数中传入数据库名称就可以了,但如果将数据库保存到指定的路径下面,都需要通过重写继承sqliteopenhelper类的构造函数中的context,因为:在阅读sqliteopenhelper.java的源码时会发现:创建数据库都是通过context的openorcreatedatabase方法实现的,如果我们需要在指定的路径下创建数据库,就需要写一个类继承context,并复写其openorcreatedatabase方法,在openorcreatedatabase方法中指定数据库存储的路径即可,下面为类sqliteopenhelper中getwritabledatabase和getreadabledatabase方法的源码,sqliteopenhelper就是通过这两个方法来创建数据库的。
/** * create and/or open a database that will be used for reading and writing. * the first time this is called, the database will be opened and * {@link #oncreate}, {@link #onupgrade} and/or {@link #onopen} will be * called. * * <p>once opened successfully, the database is cached, so you can * call this method every time you need to write to the database. * (make sure to call {@link #close} when you no longer need the database.) * errors such as bad permissions or a full disk may cause this method * to fail, but future attempts may succeed if the problem is fixed.</p> * * <p class="caution">database upgrade may take a long time, you * should not call this method from the application main thread, including * from {@link android.content.contentprovider#oncreate contentprovider.oncreate()}. * * @throws sqliteexception if the database cannot be opened for writing * @return a read/write database object valid until {@link #close} is called */ public synchronized sqlitedatabase getwritabledatabase() { if (mdatabase != null) { if (!mdatabase.isopen()) { // darn! the user closed the database by calling mdatabase.close() mdatabase = null; } else if (!mdatabase.isreadonly()) { return mdatabase; // the database is already open for business } } if (misinitializing) { throw new illegalstateexception("getwritabledatabase called recursively"); } // if we have a read-only database open, someone could be using it // (though they shouldn't), which would cause a lock to be held on // the file, and our attempts to open the database read-write would // fail waiting for the file lock. to prevent that, we acquire the // lock on the read-only database, which shuts out other users. boolean success = false; sqlitedatabase db = null; if (mdatabase != null) mdatabase.lock(); try { misinitializing = true; if (mname == null) { db = sqlitedatabase.create(null); } else { db = mcontext.openorcreatedatabase(mname, 0, mfactory, merrorhandler); } int version = db.getversion(); if (version != mnewversion) { db.begintransaction(); try { if (version == 0) { oncreate(db); } else { if (version > mnewversion) { ondowngrade(db, version, mnewversion); } else { onupgrade(db, version, mnewversion); } } db.setversion(mnewversion); db.settransactionsuccessful(); } finally { db.endtransaction(); } } onopen(db); success = true; return db; } finally { misinitializing = false; if (success) { if (mdatabase != null) { try { mdatabase.close(); } catch (exception e) { } mdatabase.unlock(); } mdatabase = db; } else { if (mdatabase != null) mdatabase.unlock(); if (db != null) db.close(); } } } /** * create and/or open a database. this will be the same object returned by * {@link #getwritabledatabase} unless some problem, such as a full disk, * requires the database to be opened read-only. in that case, a read-only * database object will be returned. if the problem is fixed, a future call * to {@link #getwritabledatabase} may succeed, in which case the read-only * database object will be closed and the read/write object will be returned * in the future. * * <p class="caution">like {@link #getwritabledatabase}, this method may * take a long time to return, so you should not call it from the * application main thread, including from * {@link android.content.contentprovider#oncreate contentprovider.oncreate()}. * * @throws sqliteexception if the database cannot be opened * @return a database object valid until {@link #getwritabledatabase} * or {@link #close} is called. */ public synchronized sqlitedatabase getreadabledatabase() { if (mdatabase != null) { if (!mdatabase.isopen()) { // darn! the user closed the database by calling mdatabase.close() mdatabase = null; } else { return mdatabase; // the database is already open for business } } if (misinitializing) { throw new illegalstateexception("getreadabledatabase called recursively"); } try { return getwritabledatabase(); } catch (sqliteexception e) { if (mname == null) throw e; // can't open a temp database read-only! log.e(tag, "couldn't open " + mname + " for writing (will try read-only):", e); } sqlitedatabase db = null; try { misinitializing = true; string path = mcontext.getdatabasepath(mname).getpath(); db = sqlitedatabase.opendatabase(path, mfactory, sqlitedatabase.open_readonly, merrorhandler); if (db.getversion() != mnewversion) { throw new sqliteexception("can't upgrade read-only database from version " + db.getversion() + " to " + mnewversion + ": " + path); } onopen(db); log.w(tag, "opened " + mname + " in read-only mode"); mdatabase = db; return mdatabase; } finally { misinitializing = false; if (db != null && db != mdatabase) db.close(); } }
通过上面的分析可以写出一个自定义的context类,该类继承context即可,但由于context中有除了openorcreatedatabase方法以外的其它抽象函数,所以建议使用非抽象类contextwrapper,该类继承自context,自定义的databasecontext类源码如下:
public class databasecontext extends contextwrapper { public databasecontext(context context){ super( context ); } /** * 获得数据库路径,如果不存在,则创建对象对象 * @param name * @param mode * @param factory */ @override public file getdatabasepath(string name) { //判断是否存在sd卡 boolean sdexist = android.os.environment.media_mounted.equals(android.os.environment.getexternalstoragestate()); if(!sdexist){//如果不存在, return null; }else{//如果存在 //获取sd卡路径 string dbdir= fileutils.getflashbpath(); dbdir += "db";//数据库所在目录 string dbpath = dbdir+"/"+name;//数据库路径 //判断目录是否存在,不存在则创建该目录 file dirfile = new file(dbdir); if(!dirfile.exists()){ dirfile.mkdirs(); } //数据库文件是否创建成功 boolean isfilecreatesuccess = false; //判断文件是否存在,不存在则创建该文件 file dbfile = new file(dbpath); if(!dbfile.exists()){ try { isfilecreatesuccess = dbfile.createnewfile();//创建文件 } catch (ioexception e) { e.printstacktrace(); } }else{ isfilecreatesuccess = true; } //返回数据库文件对象 if(isfilecreatesuccess){ return dbfile; }else{ return null; } } } /** * 重载这个方法,是用来打开sd卡上的数据库的,android 2.3及以下会调用这个方法。 * * @param name * @param mode * @param factory */ @override public sqlitedatabase openorcreatedatabase(string name, int mode, sqlitedatabase.cursorfactory factory) { sqlitedatabase result = sqlitedatabase.openorcreatedatabase(getdatabasepath(name), null); return result; } /** * android 4.0会调用此方法获取数据库。 * * @see android.content.contextwrapper#openorcreatedatabase(java.lang.string, int, * android.database.sqlite.sqlitedatabase.cursorfactory, * android.database.databaseerrorhandler) * @param name * @param mode * @param factory * @param errorhandler */ @override public sqlitedatabase openorcreatedatabase(string name, int mode, cursorfactory factory, databaseerrorhandler errorhandler) { sqlitedatabase result = sqlitedatabase.openorcreatedatabase(getdatabasepath(name), null); return result; } }
在继承sqliteopenhelper的子类的构造函数中,用databasecontext的实例替代context即可:
databasecontext dbcontext = new databasecontext(context); super(dbcontext, mdatabasename, null, version);
基于android如何实现将数据库保存到sd卡的全部内容就给大家介绍这么多,同时也非常感谢大家一直以来对网站的支持,谢谢。