Android将数据库保存到SD卡的实现

来源:互联网 发布:js视频教程 百度云 编辑:程序博客网 时间:2024/05/21 18:37

有时候为了需要,会将数据库保存到外部存储或者SD卡中(对于这种情况可以通过加密数据来避免数据被破解),比如一个应用支持多个数据,每个数据都需要有一个对应的数据库,并且数据库中的信息量特别大时,这显然更应该将数据库保存在外部存储或者SD卡中,因为RAM的大小是有限的;其次在写某些测试程序时将数据库保存在SD卡更方便查看数据库中的内容。

  Android通过SQLiteOpenHelper创建数据库时默认是将数据库保存在'/data/data/应用程序名/databases'目录下的,只需要在继承SQLiteOpenHelper类的构造函数中传入数据库名称就可以了,但如果将数据库保存到指定的路径下面,都需要通过重写继承SQLiteOpenHelper类的构造函数中的context,因为:在阅读SQLiteOpenHelper.java的源码时会发现:创建数据库都是通过Context的openOrCreateDatabase方法实现的,如果我们需要在指定的路径下创建数据库,就需要写一个类继承Context,并复写其openOrCreateDatabase方法,在openOrCreateDatabase方法中指定数据库存储的路径即可,下面为类SQLiteOpenHelper中getWritableDatabase和getReadableDatabase方法的源码,SQLiteOpenHelper就是通过这两个方法来创建数据库的。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.      * Create and/or open a database that will be used for reading and writing. 
  3.      * The first time this is called, the database will be opened and 
  4.      * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be 
  5.      * called. 
  6.      * 
  7.      * <p>Once opened successfully, the database is cached, so you can 
  8.      * call this method every time you need to write to the database. 
  9.      * (Make sure to call {@link #close} when you no longer need the database.) 
  10.      * Errors such as bad permissions or a full disk may cause this method 
  11.      * to fail, but future attempts may succeed if the problem is fixed.</p> 
  12.      * 
  13.      * <p class="caution">Database upgrade may take a long time, you 
  14.      * should not call this method from the application main thread, including 
  15.      * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. 
  16.      * 
  17.      * @throws SQLiteException if the database cannot be opened for writing 
  18.      * @return a read/write database object valid until {@link #close} is called 
  19.      */  
  20.     public synchronized SQLiteDatabase getWritableDatabase() {  
  21.         if (mDatabase != null) {  
  22.             if (!mDatabase.isOpen()) {  
  23.                 // darn! the user closed the database by calling mDatabase.close()  
  24.                 mDatabase = null;  
  25.             } else if (!mDatabase.isReadOnly()) {  
  26.                 return mDatabase;  // The database is already open for business  
  27.             }  
  28.         }  
  29.   
  30.         if (mIsInitializing) {  
  31.             throw new IllegalStateException("getWritableDatabase called recursively");  
  32.         }  
  33.   
  34.         // If we have a read-only database open, someone could be using it  
  35.         // (though they shouldn't), which would cause a lock to be held on  
  36.         // the file, and our attempts to open the database read-write would  
  37.         // fail waiting for the file lock.  To prevent that, we acquire the  
  38.         // lock on the read-only database, which shuts out other users.  
  39.   
  40.         boolean success = false;  
  41.         SQLiteDatabase db = null;  
  42.         if (mDatabase != null) mDatabase.lock();  
  43.         try {  
  44.             mIsInitializing = true;  
  45.             if (mName == null) {  
  46.                 db = SQLiteDatabase.create(null);  
  47.             } else {  
  48.                 db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);  
  49.             }  
  50.   
  51.             int version = db.getVersion();  
  52.             if (version != mNewVersion) {  
  53.                 db.beginTransaction();  
  54.                 try {  
  55.                     if (version == 0) {  
  56.                         onCreate(db);  
  57.                     } else {  
  58.                         if (version > mNewVersion) {  
  59.                             onDowngrade(db, version, mNewVersion);  
  60.                         } else {  
  61.                             onUpgrade(db, version, mNewVersion);  
  62.                         }  
  63.                     }  
  64.                     db.setVersion(mNewVersion);  
  65.                     db.setTransactionSuccessful();  
  66.                 } finally {  
  67.                     db.endTransaction();  
  68.                 }  
  69.             }  
  70.   
  71.             onOpen(db);  
  72.             success = true;  
  73.             return db;  
  74.         } finally {  
  75.             mIsInitializing = false;  
  76.             if (success) {  
  77.                 if (mDatabase != null) {  
  78.                     try { mDatabase.close(); } catch (Exception e) { }  
  79.                     mDatabase.unlock();  
  80.                 }  
  81.                 mDatabase = db;  
  82.             } else {  
  83.                 if (mDatabase != null) mDatabase.unlock();  
  84.                 if (db != null) db.close();  
  85.             }  
  86.         }  
  87.     }  
  88.   
  89.     /** 
  90.      * Create and/or open a database.  This will be the same object returned by 
  91.      * {@link #getWritableDatabase} unless some problem, such as a full disk, 
  92.      * requires the database to be opened read-only.  In that case, a read-only 
  93.      * database object will be returned.  If the problem is fixed, a future call 
  94.      * to {@link #getWritableDatabase} may succeed, in which case the read-only 
  95.      * database object will be closed and the read/write object will be returned 
  96.      * in the future. 
  97.      * 
  98.      * <p class="caution">Like {@link #getWritableDatabase}, this method may 
  99.      * take a long time to return, so you should not call it from the 
  100.      * application main thread, including from 
  101.      * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. 
  102.      * 
  103.      * @throws SQLiteException if the database cannot be opened 
  104.      * @return a database object valid until {@link #getWritableDatabase} 
  105.      *     or {@link #close} is called. 
  106.      */  
  107.     public synchronized SQLiteDatabase getReadableDatabase() {  
  108.         if (mDatabase != null) {  
  109.             if (!mDatabase.isOpen()) {  
  110.                 // darn! the user closed the database by calling mDatabase.close()  
  111.                 mDatabase = null;  
  112.             } else {  
  113.                 return mDatabase;  // The database is already open for business  
  114.             }  
  115.         }  
  116.   
  117.         if (mIsInitializing) {  
  118.             throw new IllegalStateException("getReadableDatabase called recursively");  
  119.         }  
  120.   
  121.         try {  
  122.             return getWritableDatabase();  
  123.         } catch (SQLiteException e) {  
  124.             if (mName == nullthrow e;  // Can't open a temp database read-only!  
  125.             Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);  
  126.         }  
  127.   
  128.         SQLiteDatabase db = null;  
  129.         try {  
  130.             mIsInitializing = true;  
  131.             String path = mContext.getDatabasePath(mName).getPath();  
  132.             db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY,  
  133.                     mErrorHandler);  
  134.             if (db.getVersion() != mNewVersion) {  
  135.                 throw new SQLiteException("Can't upgrade read-only database from version " +  
  136.                         db.getVersion() + " to " + mNewVersion + ": " + path);  
  137.             }  
  138.   
  139.             onOpen(db);  
  140.             Log.w(TAG, "Opened " + mName + " in read-only mode");  
  141.             mDatabase = db;  
  142.             return mDatabase;  
  143.         } finally {  
  144.             mIsInitializing = false;  
  145.             if (db != null && db != mDatabase) db.close();  
  146.         }  
  147.     }  


  通过上面的分析可以写出一个自定义的Context类,该类继承Context即可,但由于Context中有除了openOrCreateDatabase方法以外的其它抽象函数,所以建议使用非抽象类ContextWrapper,该类继承自Context,自定义的DatabaseContext类源码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class DatabaseContext extends ContextWrapper {  
  2.     public DatabaseContext(Context context){  
  3.         super( context );  
  4.     }  
  5.   
  6.     /** 
  7.      * 获得数据库路径,如果不存在,则创建对象对象 
  8.      * @param    name 
  9.      * @param    mode 
  10.      * @param    factory 
  11.      */  
  12.     @Override  
  13.     public File getDatabasePath(String name) {  
  14.         //判断是否存在sd卡  
  15.         boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState());  
  16.         if(!sdExist){//如果不存在,  
  17.             return null;  
  18.         }else{//如果存在  
  19.             //获取sd卡路径  
  20.             String dbDir= FileUtils.getFlashBPath();  
  21.             dbDir += "DB";//数据库所在目录  
  22.             String dbPath = dbDir+"/"+name;//数据库路径  
  23.             //判断目录是否存在,不存在则创建该目录  
  24.             File dirFile = new File(dbDir);  
  25.             if(!dirFile.exists()){  
  26.                 dirFile.mkdirs();  
  27.             }  
  28.   
  29.             //数据库文件是否创建成功  
  30.             boolean isFileCreateSuccess = false;   
  31.             //判断文件是否存在,不存在则创建该文件  
  32.             File dbFile = new File(dbPath);  
  33.             if(!dbFile.exists()){  
  34.                 try {                      
  35.                     isFileCreateSuccess = dbFile.createNewFile();//创建文件  
  36.                 } catch (IOException e) {  
  37.                     e.printStackTrace();  
  38.                 }  
  39.             }else{  
  40.                 isFileCreateSuccess = true;  
  41.             }  
  42.   
  43.             //返回数据库文件对象  
  44.             if(isFileCreateSuccess){  
  45.                 return dbFile;  
  46.             }else{  
  47.                 return null;  
  48.             }  
  49.         }  
  50.     }  
  51.   
  52.     /** 
  53.      * 重载这个方法,是用来打开SD卡上的数据库的,android 2.3及以下会调用这个方法。 
  54.      *  
  55.      * @param    name 
  56.      * @param    mode 
  57.      * @param    factory 
  58.      */  
  59.     @Override  
  60.     public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {  
  61.         SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);  
  62.         return result;  
  63.     }  
  64.   
  65.     /** 
  66.      * Android 4.0会调用此方法获取数据库。 
  67.      *  
  68.      * @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String, int,  
  69.      *              android.database.sqlite.SQLiteDatabase.CursorFactory, 
  70.      *              android.database.DatabaseErrorHandler) 
  71.      * @param    name 
  72.      * @param    mode 
  73.      * @param    factory 
  74.      * @param     errorHandler 
  75.      */  
  76.     @Override  
  77.     public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {  
  78.         SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);  
  79.   
  80.         return result;  
  81.     }  
  82. }  


  在继承SQLiteOpenHelper的子类的构造函数中,用DatabaseContext的实例替代context即可:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. DatabaseContext dbContext = new DatabaseContext(context);  
  2. super(dbContext, mDatabaseName, null, VERSION);  

说明:本文很大程度上参考了网络上的其它文章,在此特别感谢这些文章的作者。

参考资料:

  • Android中使用SQLiteOpenHelper管理SD卡中的数据库
0 0
原创粉丝点击