首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有完全限定DB路径名的SQLiteOpenHelper问题

具有完全限定DB路径名的SQLiteOpenHelper问题
EN

Stack Overflow用户
提问于 2011-03-16 21:55:43
回答 5查看 21K关注 0票数 24

在我的应用程序中,我使用..。

代码语言:javascript
复制
myFilesDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                      + "/Android/data/" + packageName + "/files");
myFilesDir.mkdirs();

这很好,结果是.

代码语言:javascript
复制
/mnt/sdcard/Android/data/com.mycompany.myApp/files

我需要一个SQLite DB,我想要存储在SD卡上,所以我扩展SQLiteOpenHelper如下.

代码语言:javascript
复制
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卡上创建了空DB,并由onCreate()填充它。

所以问题就在这里--这个应用程序正在测试中,可能有5到6个人,和我一样,他们运行的是Androidv2.2,一切都很好。不过,我有一个测试器,运行v2.1,当myDbHelper试图在第一次使用时创建DB时,它会崩溃,以下是.

代码语言:javascript
复制
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)

文件目录的路径是一个奇怪的路径("/nand"),因为它是内部内存,虽然不是手机的内部内存,但它是getExternalStorageDirectory()为该设备返回的路径。

我能看到三个可能的答案..。

  1. 虽然在v2.2上可以接受,但是不建议为DB名称指定一个完全限定的路径,并且在早期版本上会失败
  2. SD卡存储可以接受完全限定的路径,但是"/nand“路径被解释为‘内部’,在这种情况下只有相对路径是可以接受的。
  3. 其他我完全错过的东西

如果有上述任何或全部申请,如果有人能帮我解决这个问题,我将不胜感激。

谢谢。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-03-16 23:56:42

在历史上,您无法在SQLiteOpenHelper中使用路径。它只适用于简单的文件名。我没有意识到他们在Android2.2中放宽了这个限制。

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

抱歉的!

票数 17
EN

Stack Overflow用户

发布于 2012-02-06 23:37:12

如果您提供了一个SQLiteOpenHelper自定义 ContextClass,并且您在目标目录中有写访问权限,那么您可以使用自定义路径。

代码语言:javascript
复制
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类,它完成了所有的魔术:

代码语言:javascript
复制
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月:

在用新的Androd-4.4设备替换了我的旧Androd-2.2设备之后,我发现我的解决方案已经失效了。多亏了@damccull的回答,我才能解决这个问题。我已经更新了这个答案,所以这应该是一个工作的例子。

2017年5月更新:

统计:在200多个github项目中使用此方法

票数 47
EN

Stack Overflow用户

发布于 2013-06-23 06:43:02

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

代码语言:javascript
复制
openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags, DatabaseErrorHandler errorHandler)

这似乎是默认情况下在某些具有此方法的设备上调用的方法。

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

首先,编辑现有的方法,使其只返回调用新方法的结果。

代码语言:javascript
复制
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
        CursorFactory factory) {
    return openOrCreateDatabase(name, mode, factory, null);

}

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

代码语言:javascript
复制
@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使用的是字符串而不是文件,我使用了允许DatabaseErrorHandler对象的版本。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5332328

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档