2013-08-24 36 views
2

我已经经历了其他类似的问题,但我没有得到答案。SQLite磁盘IO异常,无法打开数据库异常

在我的Android应用程序中,我打开了一个预先构建的数据库。它位于资产文件夹中,并使用SQLiteOpenHelper进行复制(如果尚未存在)。这个类:

import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 

import android.content.Context; 
import android.database.SQLException; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteOpenHelper; 

public class ExternalDbOpenHelper extends SQLiteOpenHelper { 

public static String DB_PATH; 
public static String DB_NAME; 
public SQLiteDatabase database; 
public final Context context; 

public SQLiteDatabase getDb() { 
    return database; 
} 

public ExternalDbOpenHelper(Context context, String databaseName) { 
    super(context, databaseName, null, 1); 
    this.context = context; 

    String packageName = context.getPackageName(); 
    DB_PATH = String.format("//data//data//%s//databases//", packageName); 
    DB_NAME = databaseName; 
    openDataBase(); 
} 

public void createDataBase() { 
    boolean dbExist = checkDataBase(); 
    if (!dbExist) { 
     this.getReadableDatabase(); 
     try { 
      copyDataBase(); 
     } catch (IOException e) { 
      // Log.e(this.getClass().toString(), "Copying error"); 
      //throw new Error("Error copying database!"); 
     } 
    } else { 
     // Log.i(this.getClass().toString(), "Database already exists"); 
    } 
} 

private boolean checkDataBase() { 
    SQLiteDatabase checkDb = null; 
    try { 
     String path = DB_PATH + DB_NAME; 
     checkDb = SQLiteDatabase.openDatabase(path, null, 
       SQLiteDatabase.OPEN_READONLY); 
    } catch (SQLException e) { 
     // Log.e(this.getClass().toString(), "Error while checking db"); 
    } 

    if (checkDb != null) { 
     checkDb.close(); 
    } 
    return checkDb != null; 
} 

private void copyDataBase() throws IOException { 

    InputStream externalDbStream = context.getAssets().open(DB_NAME); 

    String outFileName = DB_PATH + DB_NAME; 

    OutputStream localDbStream = new FileOutputStream(outFileName); 

    byte[] buffer = new byte[1024]; 
    int bytesRead; 
    while ((bytesRead = externalDbStream.read(buffer)) > 0) { 
     localDbStream.write(buffer, 0, bytesRead); 
    } 

    localDbStream.close(); 
    externalDbStream.close(); 

} 

public SQLiteDatabase openDataBase() throws SQLException { 
    String path = DB_PATH + DB_NAME; 
    if (database == null) { 
     createDataBase(); 
     database = SQLiteDatabase.openDatabase(path, null, 
       SQLiteDatabase.OPEN_READWRITE); //caused by this line 
    } 
    return database; 
} 

@Override 
public synchronized void close() { 
    if (database != null) { 
     database.close(); 
    } 
    super.close(); 
} 

@Override 
public void onCreate(SQLiteDatabase db) { 
} 

@Override 
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
} 
} 

而且这是在我的活动:

private static final String DB_NAME = "myDB.sqlite"; 
private SQLiteDatabase database; 
private ExternalDbOpenHelper dbOpenHelper; 

//in onCreate() 
dbOpenHelper = new ExternalDbOpenHelper(this,DB_NAME); 
database = dbOpenHelper.openDataBase(); 

在我的应用我反复查询使用光标rawQuery数据库。我打开数据库OPEN_READONLY,因为我从不修改它。所以之前没有关闭它。 现在增加了这一点:

@Override 
protected void onDestroy() 
{ 
    dbOpenHelper.close(); 
    super.onDestroy(); 

} 

这是问题吗?我从来没有经历过SQLite磁盘IO异常,并且无法打开数据库异常(没有dbOpenHelper.close();,每次在崩溃报告中都报告过)。这两次的应用程序坠毁,机上推出,并在theExternalDbOpenHelper类行

database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);

中的openDatabase

()引起的。

我无法重现错误。报告它的两台设备的设备名称为“其他”并且为空。

android.database.sqlite.SQLiteCantOpenDatabaseException 
in android.database.sqlite.SQLiteDatabase.dbopen: 

java.lang.RuntimeException: Unable to start activity  ComponentInfo{com.technicosa.unjumble/com.technicosa.unjumble.MainActivity}: android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981) 
at android.app.ActivityThread.access$600(ActivityThread.java:123) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:137) 
at android.app.ActivityThread.main(ActivityThread.java:4424) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:511) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 
at dalvik.system.NativeStart.main(Native Method) 
Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file 
at android.database.sqlite.SQLiteDatabase.dbopen(Native Method) 
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1013) 
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986) 
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1024) 
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986) 
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:962) 
at com.technicosa.unjumble.dbhelper.ExternalDbOpenHelper.openDataBase(ExternalDbOpenHelper.java:90) 
at com.technicosa.unjumble.dbhelper.ExternalDbOpenHelper.<init>(ExternalDbOpenHelper.java:33) 
at com.technicosa.unjumble.MainActivity.onCreate(MainActivity.java:131) 
at android.app.Activity.performCreate(Activity.java:4465) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920) 
... 11 more 
+0

[这](http://stackoverflow.com/a/4828540/739270)可以是共享的崩溃日志将帮助不大,而调试代码的原因 –

+1

。请分享报告的日志。我最怀疑代码的是数据库**路径**,您使用它的方式可能会导致许多设备(如Nexus S,HTC)的IO。尝试使用'Environment.getDataDirectory()+“/ data/YOUR_PACKAGE/databases /”;'作为数据目录可能会有所不同。 –

+0

我已发布堆栈跟踪。 –

回答

0

看这这工作就像一个魅力

public class SmartOpenHelper extends SQLiteOpenHelper { 

    private Context context; 
    private SQLiteDatabase myDataBase; 
    private String DB_SQL; 
    private SmartVersionHandler smartVersionHandler; 

    SmartOpenHelper(Context context, String dbname, int dbversion, String dbSqlName, SmartVersionHandler smartVersionHandler) throws IOException { 
     super(context, dbname, null, dbversion); 
     this.context = context; 
     this.DB_SQL = dbSqlName; 
     this.smartVersionHandler = smartVersionHandler; 
    } 

    @Override 
    public void onCreate(SQLiteDatabase db) { 
     try { 
      BufferedInputStream inStream = new BufferedInputStream(context.getAssets().open(DB_SQL)); 
      String sql = ""; 
      int character = -2; 
      do { 
       character = inStream.read(); 
       if ((character != -1) && (character != -2)) 
        sql += (char) character; 
       else 
        break; 
      } while (true); 
      System.out.println("onCreate DB SQL = " + sql.split("\n")); 
      String[] arrSQL = sql.split("\n"); 

      for (int i = 0; i < arrSQL.length; i++) { 
       db.execSQL(arrSQL[i]); 
      } 

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     if (this.smartVersionHandler != null) { 
      this.smartVersionHandler.onInstalling(SmartApplication.REF_SMART_APPLICATION); 
     } 
    } 

    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 

     try { 
      BufferedInputStream inStream = new BufferedInputStream(context.getAssets().open(DB_SQL)); 
      String sql = ""; 
      int character = -2; 
      do { 
       character = inStream.read(); 
       if ((character != -1) && (character != -2)) 
        sql += (char) character; 
       else 
        break; 
      } while (true); 

      System.out.println("onUpgrade DB SQL = " + sql.split("\n")); 
      String[] arrSQL = sql.split("\n"); 
      for (int i = 0; i < arrSQL.length; i++) { 
       db.execSQL(arrSQL[i]); 
      } 

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     if (this.smartVersionHandler != null) { 
      this.smartVersionHandler.onUpgrading(oldVersion, newVersion, SmartApplication.REF_SMART_APPLICATION); 
     } 
    } 

    public SQLiteDatabase getOpenDatabase() { 
     return myDataBase; 
    } 

    public synchronized void close() { 
     if (myDataBase != null) { 
      myDataBase.close(); 
     } 
     super.close(); 
    } 

} 
+4

我需要找出我的代码有什么问题。 –

0

我真的不认为这是一个很好的解决问题的方法。 SQLiteOpenHelper并不意味着要这样使用。您应该将数据库复制到助手类之外,然后初始化应该为您提供服务的助手,以便获取SQLiteDatabase(通过getReadableDatabase和/或getWritableDatabase)。

但是,阅读代码我不明白为什么在createDataBase函数中的copyDataBase之前调用getReadableDatabase。您还应该按照hotveryspicy的建议检查数据库路径。

您能否发布异常的堆栈跟踪?

尝试用这个编辑:

public class ExternalDbOpenHelper extends SQLiteOpenHelper { 

public static String DB_PATH; 
public static String DB_NAME; 
public SQLiteDatabase database; 
public final Context context; 

public SQLiteDatabase getDb() { 
    return database; 
} 

public ExternalDbOpenHelper(Context context, String databaseName) { 
    super(context, databaseName, null, 1); 
    this.context = context; 

    String packageName = context.getPackageName(); 
    DB_PATH = String.format("%s//data//%s//databases//", Environment.getDataDirectory(), packageName); // as per hotveryspicy comment 
    DB_NAME = databaseName; 
    openDataBase(); 
} 

public void createDataBase() { 
    boolean dbExist = checkDataBase(); 
    if (!dbExist) { 
     //this.getReadableDatabase(); why do you call getReadableDatabase() here? 
     try { 
      copyDataBase(); 
     } catch (IOException e) { 
      // Log.e(this.getClass().toString(), "Copying error"); 
      //throw new Error("Error copying database!"); 
     } 
    } else { 
     // Log.i(this.getClass().toString(), "Database already exists"); 
    } 
} 

private boolean checkDataBase() { 
    SQLiteDatabase checkDb = null; 
    try { 
     String path = DB_PATH + DB_NAME; 
     checkDb = SQLiteDatabase.openDatabase(path, null, 
       SQLiteDatabase.OPEN_READONLY); 
    } catch (SQLException e) { 
     // Log.e(this.getClass().toString(), "Error while checking db"); 
    } 

    if (checkDb != null) { 
     checkDb.close(); 
    } 
    return checkDb != null; 
} 

private void copyDataBase() throws IOException { 

    InputStream externalDbStream = context.getAssets().open(DB_NAME); 

    String outFileName = DB_PATH + DB_NAME; 

    OutputStream localDbStream = new FileOutputStream(outFileName); 

    byte[] buffer = new byte[1024]; 
    int bytesRead; 
    while ((bytesRead = externalDbStream.read(buffer)) > 0) { 
     localDbStream.write(buffer, 0, bytesRead); 
    } 

    localDbStream.close(); 
    externalDbStream.close(); 

} 

public SQLiteDatabase openDataBase() throws SQLException { 
    String path = DB_PATH + DB_NAME; 
    if (database == null) { 
     createDataBase(); 
     //database = SQLiteDatabase.openDatabase(path, null, 
       //SQLiteDatabase.OPEN_READWRITE); 

     database = getWritableDatabase(); // <- try with this   
    } 
    return database; 
} 

@Override 
public synchronized void close() { 
    if (database != null) { 
     database.close(); 
    } 
    super.close(); 
} 

@Override 
public void onCreate(SQLiteDatabase db) { 
} 

@Override 
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
} 
} 
+0

我已经发布了堆栈跟踪。 –

+0

它似乎没有找到数据库。你测试过我的编码吗? –

+0

是的。安装后第一次应用程序崩溃,或者如果我在应用程序设置中清除了数据' –