基于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就是通过这两个方法来创建数据库的。
/**
*Createand/oropenadatabasethatwillbeusedforreadingandwriting.
*Thefirsttimethisiscalled,thedatabasewillbeopenedand
*{@link#onCreate},{@link#onUpgrade}and/or{@link#onOpen}willbe
*called.
*
*<p>Onceopenedsuccessfully,thedatabaseiscached,soyoucan
*callthismethodeverytimeyouneedtowritetothedatabase.
*(Makesuretocall{@link#close}whenyounolongerneedthedatabase.)
*Errorssuchasbadpermissionsorafulldiskmaycausethismethod
*tofail,butfutureattemptsmaysucceediftheproblemisfixed.</p>
*
*<pclass="caution">Databaseupgrademaytakealongtime,you
*shouldnotcallthismethodfromtheapplicationmainthread,including
*from{@linkandroid.content.ContentProvider#onCreateContentProvider.onCreate()}.
*
*@throwsSQLiteExceptionifthedatabasecannotbeopenedforwriting
*@returnaread/writedatabaseobjectvaliduntil{@link#close}iscalled
*/
publicsynchronizedSQLiteDatabasegetWritableDatabase(){
if(mDatabase!=null){
if(!mDatabase.isOpen()){
//darn!theuserclosedthedatabasebycallingmDatabase.close()
mDatabase=null;
}elseif(!mDatabase.isReadOnly()){
returnmDatabase;//Thedatabaseisalreadyopenforbusiness
}
}
if(mIsInitializing){
thrownewIllegalStateException("getWritableDatabasecalledrecursively");
}
//Ifwehavearead-onlydatabaseopen,someonecouldbeusingit
//(thoughtheyshouldn't),whichwouldcausealocktobeheldon
//thefile,andourattemptstoopenthedatabaseread-writewould
//failwaitingforthefilelock.Topreventthat,weacquirethe
//lockontheread-onlydatabase,whichshutsoutotherusers.
booleansuccess=false;
SQLiteDatabasedb=null;
if(mDatabase!=null)mDatabase.lock();
try{
mIsInitializing=true;
if(mName==null){
db=SQLiteDatabase.create(null);
}else{
db=mContext.openOrCreateDatabase(mName,0,mFactory,mErrorHandler);
}
intversion=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;
returndb;
}finally{
mIsInitializing=false;
if(success){
if(mDatabase!=null){
try{mDatabase.close();}catch(Exceptione){}
mDatabase.unlock();
}
mDatabase=db;
}else{
if(mDatabase!=null)mDatabase.unlock();
if(db!=null)db.close();
}
}
}
/**
*Createand/oropenadatabase.Thiswillbethesameobjectreturnedby
*{@link#getWritableDatabase}unlesssomeproblem,suchasafulldisk,
*requiresthedatabasetobeopenedread-only.Inthatcase,aread-only
*databaseobjectwillbereturned.Iftheproblemisfixed,afuturecall
*to{@link#getWritableDatabase}maysucceed,inwhichcasetheread-only
*databaseobjectwillbeclosedandtheread/writeobjectwillbereturned
*inthefuture.
*
*<pclass="caution">Like{@link#getWritableDatabase},thismethodmay
*takealongtimetoreturn,soyoushouldnotcallitfromthe
*applicationmainthread,includingfrom
*{@linkandroid.content.ContentProvider#onCreateContentProvider.onCreate()}.
*
*@throwsSQLiteExceptionifthedatabasecannotbeopened
*@returnadatabaseobjectvaliduntil{@link#getWritableDatabase}
*or{@link#close}iscalled.
*/
publicsynchronizedSQLiteDatabasegetReadableDatabase(){
if(mDatabase!=null){
if(!mDatabase.isOpen()){
//darn!theuserclosedthedatabasebycallingmDatabase.close()
mDatabase=null;
}else{
returnmDatabase;//Thedatabaseisalreadyopenforbusiness
}
}
if(mIsInitializing){
thrownewIllegalStateException("getReadableDatabasecalledrecursively");
}
try{
returngetWritableDatabase();
}catch(SQLiteExceptione){
if(mName==null)throwe;//Can'topenatempdatabaseread-only!
Log.e(TAG,"Couldn'topen"+mName+"forwriting(willtryread-only):",e);
}
SQLiteDatabasedb=null;
try{
mIsInitializing=true;
Stringpath=mContext.getDatabasePath(mName).getPath();
db=SQLiteDatabase.openDatabase(path,mFactory,SQLiteDatabase.OPEN_READONLY,
mErrorHandler);
if(db.getVersion()!=mNewVersion){
thrownewSQLiteException("Can'tupgraderead-onlydatabasefromversion"+
db.getVersion()+"to"+mNewVersion+":"+path);
}
onOpen(db);
Log.w(TAG,"Opened"+mName+"inread-onlymode");
mDatabase=db;
returnmDatabase;
}finally{
mIsInitializing=false;
if(db!=null&&db!=mDatabase)db.close();
}
}
通过上面的分析可以写出一个自定义的Context类,该类继承Context即可,但由于Context中有除了openOrCreateDatabase方法以外的其它抽象函数,所以建议使用非抽象类ContextWrapper,该类继承自Context,自定义的DatabaseContext类源码如下:
publicclassDatabaseContextextendsContextWrapper{
publicDatabaseContext(Contextcontext){
super(context);
}
/**
*获得数据库路径,如果不存在,则创建对象对象
*@paramname
*@parammode
*@paramfactory
*/
@Override
publicFilegetDatabasePath(Stringname){
//判断是否存在sd卡
booleansdExist=android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState());
if(!sdExist){//如果不存在,
returnnull;
}else{//如果存在
//获取sd卡路径
StringdbDir=FileUtils.getFlashBPath();
dbDir+="DB";//数据库所在目录
StringdbPath=dbDir+"/"+name;//数据库路径
//判断目录是否存在,不存在则创建该目录
FiledirFile=newFile(dbDir);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//数据库文件是否创建成功
booleanisFileCreateSuccess=false;
//判断文件是否存在,不存在则创建该文件
FiledbFile=newFile(dbPath);
if(!dbFile.exists()){
try{
isFileCreateSuccess=dbFile.createNewFile();//创建文件
}catch(IOExceptione){
e.printStackTrace();
}
}else{
isFileCreateSuccess=true;
}
//返回数据库文件对象
if(isFileCreateSuccess){
returndbFile;
}else{
returnnull;
}
}
}
/**
*重载这个方法,是用来打开SD卡上的数据库的,android2.3及以下会调用这个方法。
*
*@paramname
*@parammode
*@paramfactory
*/
@Override
publicSQLiteDatabaseopenOrCreateDatabase(Stringname,intmode,SQLiteDatabase.CursorFactoryfactory){
SQLiteDatabaseresult=SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name),null);
returnresult;
}
/**
*Android4.0会调用此方法获取数据库。
*
*@seeandroid.content.ContextWrapper#openOrCreateDatabase(java.lang.String,int,
*android.database.sqlite.SQLiteDatabase.CursorFactory,
*android.database.DatabaseErrorHandler)
*@paramname
*@parammode
*@paramfactory
*@paramerrorHandler
*/
@Override
publicSQLiteDatabaseopenOrCreateDatabase(Stringname,intmode,CursorFactoryfactory,DatabaseErrorHandlererrorHandler){
SQLiteDatabaseresult=SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name),null);
returnresult;
}
}
在继承SQLiteOpenHelper的子类的构造函数中,用DatabaseContext的实例替代context即可:
DatabaseContextdbContext=newDatabaseContext(context); super(dbContext,mDatabaseName,null,VERSION);
基于Android如何实现将数据库保存到SD卡的全部内容就给大家介绍这么多,同时也非常感谢大家一直以来对毛票票网站的支持,谢谢。