错误的SQLite错误?代码14

时间:2018-05-17 08:26:23

标签: java android database sqlite

我遇到了一个奇怪的问题。我正在查询数据库表中的单词列表,并能够获取列表。但我无法更新该表。奇怪的是,logcat显示Code 14错误。我卸载了应用程序,重新运行它以便db被新复制,但没有任何改变。

这是dbHandler代码:

public VocabDatabase(Context context) {

    super(context, DB_NAME, null, 1);
    this.myContext = context;
    this.DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
}

/**
 * Creates a empty database on the system and rewrites it with your own database.
 */
public void createDataBase() throws IOException {

    boolean dbExist = checkDataBase();

    if (dbExist) {
        //do nothing - database already exist
    } else {
        //By calling this method and empty database will be created into the default system path
        //of your application so we are gonna be able to overwrite that database with our database.
        this.getWritableDatabase();

        try {
            copyDataBase();
        } catch (IOException e) {
            throw new Error("Error copying database");

        }

    }

}

/**
 * Check if the database already exist to avoid re-copying the file each time you open the application.
 *
 * @return true if it exists, false if it doesn't
 */
private boolean checkDataBase() {
    this.getReadableDatabase();

    SQLiteDatabase checkDB = null;

    try {
        String myPath = DB_PATH;
        checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);

    } catch (SQLiteException e) {
        e.printStackTrace();
    }

    if (checkDB != null) {

        checkDB.close();

    }

    return (checkDB != null) ? true : false;
}
private boolean checkDataBaseAlt(){
    File chkdb = new File(DB_PATH);
    return chkdb.exists();
}

/**
 * Copies your database from your local assets-folder to the just created empty database in the
 * system folder, from where it can be accessed and handled.
 * This is done by transfering bytestream.
 */
private void copyDataBase() throws IOException {

    //Open your local db as the input stream
    InputStream myInput = myContext.getAssets().open(DB_NAME);

    // Path to the just created empty db
    String outFileName = DB_PATH + DB_NAME;

    //Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(outFileName);

    //transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }

    //Close the streams
    myOutput.flush();
    myOutput.close();
    myInput.close();

}

public void openDataBase() throws SQLException {

    //Open the database
    String myPath = DB_PATH + DB_NAME;
    myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);

}

@Override
public synchronized void close() {

    if (myDataBase != null)
        myDataBase.close();

    super.close();

}

@Override
public void onCreate(SQLiteDatabase db) {

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (newVersion > oldVersion) {
        try {
            copyDataBase();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// Add your public helper methods to access and get content from the database.
// You could return cursors by doing "return myDataBase.query(....)" so it'd be easy
// to you to create adapters for your views.

//add your public methods for insert, get, delete and update data in database.

public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
}

public long insert(String table, String nullColumnHack, ContentValues contentValues) {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.insert(table, nullColumnHack, contentValues);
}

public Cursor rawQuery(String string, String[] selectionArguments) {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.rawQuery(string, selectionArguments);
}
public long update(String table, ContentValues contentValues, String whereClause, String[] whereArgs) {
    SQLiteDatabase db = this.getWritableDatabase();
    return db.update(table, contentValues, whereClause, whereArgs);
}

}

这是服务代码:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    vdb = new VocabDatabase(this);
    Toast.makeText(this, "Service started", Toast.LENGTH_SHORT).show();
    createDB();
    queryDB();
    return super.onStartCommand(intent, flags, startId);
}
private void createDB() {
    vdb.getWritableDatabase();
    try {
        vdb.createDataBase();
        vdb.openDataBase();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public void queryDB() {
    String vet = "";
    ArrayList<String> lister = new ArrayList<>();
    Cursor cr = vdb.query(TABLE_NAME, null, null, null, null, null, null);
if (cr.moveToFirst()) {
        do {
            lister.add(cr.getString(0));
        } while (cr.moveToNext());
    }
cr.close();
String vet="";
for (String v : lister) {
            vet += v + "\t";
        }
}

String-vet显示在toast中,我可以看到表格第一列中的所有单词。但是我无法更新行。

private void updateInDatabase(String up, String pot) {
    ContentValues conval = new ContentValues();
    conval.put(pot, "1");
    try {
        long res = vdb.update(TABLE_NAME, conval, "Word=?", new String[]{up});
    } catch (Exception e) {

    }
}

我已经给予了存储权限并对其进行了双重检查。

清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.hack.corrector">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".scrapeservice"/>
</application>

错误日志:

05-17 13:39:04.614 28088-28088/com.example.hack.corrector E/SQLiteLog: (14) cannot open file at line 31282 of [5a3022e081]
05-17 13:39:04.615 28088-28088/com.example.hack.corrector E/SQLiteLog: (14) os_unix.c:31282: (21) open(/data/user/0/com.example.hack.corrector/databases/) - 
05-17 13:39:04.616 28088-28088/com.example.hack.corrector E/SQLiteDatabase: Failed to open database '/data/user/0/com.example.hack.corrector/databases/'.
    android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
        at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:207)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:191)
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
        at com.example.hakc.corrector.VocabDatabase.checkDataBase(VocabDatabase.java:81)
        at com.example.hakc.corrector.VocabDatabase.createDataBase(VocabDatabase.java:48)
        at com.example.hakc.corrector.scrapeservice.createDB(scrapeservice.java:48)
        at com.example.hakc.corrector.scrapeservice.onStartCommand(scrapeservice.java:39)
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3049)
        at android.app.ActivityThread.access$2300(ActivityThread.java:154)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1479)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:157)
        at android.app.ActivityThread.main(ActivityThread.java:5571)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:745)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:635)
05-17 1

4 个答案:

答案 0 :(得分:2)

如果您在API级别23或更高级别上运行应用程序,则会出现此问题,因为此处引入了新的实时权限模型。在这些版本中,用户在应用运行时向应用授予权限

要在运行时获取权限,您必须请求用户。您可以通过以下方式执行此操作:

请求权限

String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, REQUEST_CODE); //REQUEST_CODE can be any Integer value

并检查权限结果

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
       case REQUEST_CODE:
         if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
           //Permission granted.
           //Now you can try your database creating and writing stuff.

         }
       else{
           //Permission denied.
         }
        break;
    }
}

如果这不能解决您的问题,请转到此链接。对于同样的问题,解决方案的数量在这里。

android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database

答案 1 :(得分:2)

就像异常告诉:

  

无法打开数据库&#39; /data/user/0/com.example.hack.corrector/databases /'。

您正尝试使用设置为

的变量0.0打开SQLite DB
DB_PATH

您不需要设置文件名,只需设置目录路径。 this.DB_PATH = context.getApplicationInfo().dataDir + "/databases/"; 是一个目录。

为您的路径设置文件名:

databases/

编辑: 你有两个数据库名称变量......你正在使用this.DB_PATH = context.getApplicationInfo().dataDir + "/databases/my_db"; 打开与帮助程序的连接(在DB_NAME构造函数中),然后在你使用的super检查方法中。

所以你可以从助手

获得连接
DB_PATH

但未能与

建立联系
this.getWritableDatabase(); //Using the DB at `DB_NAME`

因为您在此使用String myPath = DB_PATH; SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE); 而不是DB_PATH。您正在使用2个不同的数据库路径。

答案 2 :(得分:1)

我相信你会发现 无法打开数据库错误 实际上并不是失败,而是一个问题的指示。这就是checkdataBase方法遇到的开放错误,因为它无法像消息所说的那样打开数据库。这是因为它被困,不会导致失败。

相反,checkDatabase方法返回false,因为它无法打开数据库,因此每次运行App时都会从资产中复制数据库。因此,撤消先前运行的任何更改。

使用两行代码诊断/调试它很简单。

  1. 添加Log.d("DBEXISTCHK", "Method checkdataBase returned" + String.valueOf(dbExist));
  2. 按照: -

    /**
     * Creates a empty database on the system and rewrites it with your own database.
     */
    public void createDataBase() throws IOException {
    
        boolean dbExist = checkDataBase();
        Log.d("DBEXISTCHK", "Method checkdataBase returned" + String.valueOf(dbExist)); //<<<<<<<<<< ADDED
    
        if (dbExist) {
            //do nothing - database already exist
        } else {
            //By calling this method and empty database will be created into the default system path
            //of your application so we are gonna be able to overwrite that database with our database.
            this.getWritableDatabase();
            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        }
    }
    

    1. 添加Log.d("DBCOPY","Database is being copied from the Assets.");
    2. 按照: -

      private void copyDataBase() throws IOException {
      
          Log.d("DBCOPY","Database is being copied from the Assets."); //<<<<<<<<<< ADDED
      
          //Open your local db as the input stream
          InputStream myInput = myContext.getAssets().open(DB_NAME);
      
          // Path to the just created empty db
          String outFileName = DB_PATH + DB_NAME;
      
          //Open the empty db as the output stream
          OutputStream myOutput = new FileOutputStream(outFileName);
      
          //transfer bytes from the inputfile to the outputfile
          byte[] buffer = new byte[1024];
          int length = 0;
          while ((length = myInput.read(buffer)) > 0) {
              myOutput.write(buffer, 0, length);
          }
      
          //Close the streams
          myOutput.flush();
          myOutput.close();
          myInput.close();
      
      }
      

      结果日志将类似于(注意最后两行): -

      05-17 10:12:22.591 1152-1152/bcdbfa.basiccopydbfromassets E/SQLiteLog: (14) cannot open file at line 30174 of [00bb9c9ce4]
          (14) os_unix.c:30174: (21) open(/data/data/bcdbfa.basiccopydbfromassets/databases/) - 
      05-17 10:12:22.591 1152-1152/bcdbfa.basiccopydbfromassets E/SQLiteDatabase: Failed to open database '/data/data/bcdbfa.basiccopydbfromassets/databases/'.
          android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
              at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
              at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
              at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
              at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
              at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
              at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
              at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
              at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
              at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
              at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
              at bcdbfa.basiccopydbfromassets.VocabDatabase.checkDataBase(VocabDatabase.java:85)
              at bcdbfa.basiccopydbfromassets.VocabDatabase.createDataBase(VocabDatabase.java:45)
              at bcdbfa.basiccopydbfromassets.MainActivity.onCreate(MainActivity.java:18)
              at android.app.Activity.performCreate(Activity.java:5008)
              at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
              at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
              at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
              at android.app.ActivityThread.access$600(ActivityThread.java:130)
              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
              at android.os.Handler.dispatchMessage(Handler.java:99)
              at android.os.Looper.loop(Looper.java:137)
              at android.app.ActivityThread.main(ActivityThread.java:4745)
              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:786)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
              at dalvik.system.NativeStart.main(Native Method)
      05-17 10:12:22.591 1152-1152/bcdbfa.basiccopydbfromassets W/System.err: android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
              at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
              at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
              at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
              at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
              at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
              at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
              at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
              at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
      05-17 10:12:22.599 1152-1152/bcdbfa.basiccopydbfromassets W/System.err:     at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
              at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
              at bcdbfa.basiccopydbfromassets.VocabDatabase.checkDataBase(VocabDatabase.java:85)
              at bcdbfa.basiccopydbfromassets.VocabDatabase.createDataBase(VocabDatabase.java:45)
              at bcdbfa.basiccopydbfromassets.MainActivity.onCreate(MainActivity.java:18)
              at android.app.Activity.performCreate(Activity.java:5008)
              at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
              at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
              at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
              at android.app.ActivityThread.access$600(ActivityThread.java:130)
              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
              at android.os.Handler.dispatchMessage(Handler.java:99)
              at android.os.Looper.loop(Looper.java:137)
              at android.app.ActivityThread.main(ActivityThread.java:4745)
              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:786)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
              at dalvik.system.NativeStart.main(Native Method)
      05-17 10:12:22.599 1152-1152/bcdbfa.basiccopydbfromassets D/DBEXISTCHK: Method checkdataBase returnedfalse
      05-17 10:12:22.599 1152-1152/bcdbfa.basiccopydbfromassets D/DBCOPY: Database is being copied from the Assets.
      

      修复

      修复更简单,只需改变: -

          String myPath = DB_PATH;
      

          String myPath = DB_PATH + DB_NAME;
      

      checkDatabase方法中。

      在这种情况下,日志中的结果(如果保留日志记录)将是: -

      05-17 10:30:49.809 1265-1265/? D/DBEXISTCHK: Method checkdataBase returnedtrue
      

      其他

      重新评论

        

      仅创建具有相同名称的空数据库。

      这是因为您在调用getWritableDatabase方法之前在createDB方法中调用createDatabase。也就是说,如果没有数据库,getWritableDatabase将创建一个空数据库(条形图为sqlite_master表,android为android_metadata表),然后调用onCreate方法。这就解释了为什么当完整路径正确时,数据库的副本被绕过并且数据库空数据库因此而存在。

      然后是评论

        

      但是当我将'myPath'改回myPath = DB_Path时;整个   数据库被复制但问题仍然存在。

      当路径不正确时,如前所述,checkDatabase方法将始终返回false,因此调用copyDatabase方法,这将在所使用的路径正确时起作用。

      所以

      private void createDB() {
          vdb.getWritableDatabase(); //<<<<<<<<<< The villainous line
          try {
              vdb.createDataBase();
              vdb.openDataBase();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      

      应该是

      private void createDB() {
          try {
              vdb.createDataBase();
              vdb.openDataBase();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      

      显然,所有路径都是包含数据库名称的完整路径。

答案 3 :(得分:0)

我已经找到了问题所在。它位于完成查询的服务中。在createDB()方法中,我使用了vdb.getWritableDatabase()。当我评论它时,查询工作顺利。复制整个数据库,并正在完美地处理查询。我需要检查“更新”。虽然..我真诚地感谢你们听到我们并花时间帮助我解决这个问题。感谢您的回答,我已经了解了如何正确地沟通数据库。