基于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卡的全部内容就给大家介绍这么多,同时也非常感谢大家一直以来对毛票票网站的支持,谢谢。