具有完全限定的DBpath名的SQLiteOpenHelper问题

在我的应用程序中,我使用…

myFilesDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + packageName + "/files"); myFilesDir.mkdirs(); 

这很好,结果是…

 /mnt/sdcard/Android/data/com.mycompany.myApp/files 

我需要一个SQLite数据库,我想存储在SD卡上,所以我扩展SQLiteOpenHelper如下…

 public class myDbHelper extends SQLiteOpenHelper { public myDbHelper(Context context, String name, CursorFactory factory, int version) { // NOTE I prefix the full path of my files directory to 'name' super(context, myFilesDir + "/" + name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { // Create tables and populate with default data... } } 

到目前为止这么好 – 我第一次调用getReadableDatabase()getWriteableDatabase()在SD卡上创build空的数据库, onCreate()填充它。

所以这里的问题 – 应用程序正在进行testing,也许5或6人,像我一样,他们正在运行Android v2.2,一切正常。 我有一个testing者,但是,运行v2.1,当myDbHelper尝试创build数据库首次使用时,崩溃与以下…

 E/AndroidRuntime( 3941): Caused by: java.lang.IllegalArgumentException: File /nand/Android/data/com.mycompany.myApp/files/myApp-DB.db3 contains a path separator E/AndroidRuntime( 3941): at android.app.ApplicationContext.makeFilename(ApplicationContext.java:1445) E/AndroidRuntime( 3941): at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:473) E/AndroidRuntime( 3941): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193) E/AndroidRuntime( 3941): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98) E/AndroidRuntime( 3941): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158) 

文件目录的path是一个奇怪的(“/ nand”),因为它是内部存储器,尽pipe不是电话本身的内部存储器,但它是getExternalStorageDirectory()返回的path。

我可以看到三个可能的答案…

  1. 尽pipe在v2.2中可以接受,但不build议为数据库名称指定完全限定的path,并且在早期版本上会失败
  2. 完全合格的path可用于SD卡存储,但“/ nand”path被解释为“内部”,在这种情况下只能使用相对path
  3. 还有一些我完全错过的东西

如果任何或所有上述适用,我会很感激,如果有人可以帮助我如何处理这个问题。

谢谢。

Solutions Collecting From Web of "具有完全限定的DBpath名的SQLiteOpenHelper问题"

从历史上看,您一直无法使用SQLiteOpenHelperpath。 它只处理简单的文件名。 我没有意识到,他们放松了在Android 2.2的限制。

如果您希望使用SD卡上的数据库,并且希望支持Android 2.1及更早版本,则不能使用SQLiteOpenHelper

抱歉!

如果您提供自定义的 ContextClass ,并且您在目标目录中有写入权限,则可以使用带有自定义path的SQLiteOpenHelper

 public class DatabaseHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 3; ..... DatabaseHelper(final Context context, String databaseName) { super(new DatabaseContext(context), databaseName, null, DATABASE_VERSION); } } 

这里是自定义的DatabaseContext类,它完成了所有的魔术:

 class DatabaseContext extends ContextWrapper { private static final String DEBUG_CONTEXT = "DatabaseContext"; public DatabaseContext(Context base) { super(base); } @Override public File getDatabasePath(String name) { File sdcard = Environment.getExternalStorageDirectory(); String dbfile = sdcard.getAbsolutePath() + File.separator+ "databases" + File.separator + name; if (!dbfile.endsWith(".db")) { dbfile += ".db" ; } File result = new File(dbfile); if (!result.getParentFile().exists()) { result.getParentFile().mkdirs(); } if (Log.isLoggable(DEBUG_CONTEXT, Log.WARN)) { Log.w(DEBUG_CONTEXT, "getDatabasePath(" + name + ") = " + result.getAbsolutePath()); } return result; } /* this version is called for android devices >= api-11. thank to @damccull for fixing this. */ @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) { return openOrCreateDatabase(name,mode, factory); } /* this version is called for android devices < api-11 */ @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) { SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null); // SQLiteDatabase result = super.openOrCreateDatabase(name, mode, factory); if (Log.isLoggable(DEBUG_CONTEXT, Log.WARN)) { Log.w(DEBUG_CONTEXT, "openOrCreateDatabase(" + name + ",,) = " + result.getPath()); } return result; } } 

2012年6月更新:
这是如何工作的(@barry问题):

正常的Android应用程序有相对于应用程序文件夹的本地数据库文件。 通过使用覆盖getDatabasePath()的客户上下文,数据库现在相对于SD卡上的不同目录。

2015年2月更新:
用新的android-4.4设备replace旧的android-2.2设备后,我发现我的解决scheme不再工作。 感谢@ damccull-s答案我能够解决它。 我已经更新了这个答案,所以这应该是一个工作的例子了。

2017年5月更新:

统计:这个问题已经在200多个github项目中使用

k3b的答案是真棒。 这让我工作。 但是,在使用API​​级别为11或更高的设备上,您可能会看到它停止工作。 这是因为添加了新版本的openOrCreateDatabase()方法。 现在它包含以下签名:

 openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags, DatabaseErrorHandler errorHandler) 

这似乎是在某些使用此方法的设备上默认调用的方法。

为了使这种方法在这些设备上工作,您需要进行以下更改:

首先,编辑你现有的方法,以便它只是返回一个调用的结果给新的方法。

 @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { return openOrCreateDatabase(name, mode, factory, null); } 

其次,用下面的代码添加新的覆盖。

 @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) { SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(),null,errorHandler); return result; } 

这段代码和k3b的代码非常相似,但是请注意,SQLiteDatabase.openOrCreateDatabase接受一个String而不是一个File,而且我使用了允许DatabaseErrorHandler对象的版本。

user2371653的回答是非常好的。 但是我发现了一个问题:

 @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { return openOrCreateDatabase(name, mode, factory, null); } 

这可能会导致crasd,如果安装在Android 2.x的应用程序

所以我们可以像这样修改它

 @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { return super.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(), mode, factory); } 

因为android 2.x没有api

 openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) 

在我看来,我发现这方面的更好的解决scheme( SD卡上的SQLite数据库 ),并想通知你。 注意构造函数中的条目。

 public class TestDB extends SQLiteOpenHelper { private static final String DATABASE_NAME = "usertest.db"; private static final int DATABASE_VERSION = 1; public TestDB (Context context){ super(context, context.getExternalFilesDir(null).getAbsolutePath() + "/" + DATABASE_NAME, null, DATABASE_VERSION ); } ... } 

从用户网站引用:
“它将在sdcard:/ sdcard / Android / data / [your_package_name] /文件的应用程序文件夹中创build数据库,这样数据库将被android视为应用程序的一部分,如果用户卸载应用“。

“我的应用我有一个庞大的数据库,在大多数情况下,它不适合旧手机的内部存储器,例如HTC Desire。它在SD卡上运行良好,大多数应用程序本身也被”移动到SD卡“,所以不要担心关于数据库不可访问,因为应用程序将无法自己访问。“