我遇到了一个奇怪的问题。我正在查询数据库表中的单词列表,并能够获取列表。但我无法更新该表。奇怪的是,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
答案 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;
}
}
如果这不能解决您的问题,请转到此链接。对于同样的问题,解决方案的数量在这里。
答案 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时都会从资产中复制数据库。因此,撤消先前运行的任何更改。
使用两行代码诊断/调试它很简单。
Log.d("DBEXISTCHK", "Method checkdataBase returned" + String.valueOf(dbExist));
按照: -
/**
* 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");
}
}
}
和
Log.d("DBCOPY","Database is being copied from the Assets.");
按照: -
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()
。当我评论它时,查询工作顺利。复制整个数据库,并正在完美地处理查询。我需要检查“更新”。虽然..我真诚地感谢你们听到我们并花时间帮助我解决这个问题。感谢您的回答,我已经了解了如何正确地沟通数据库。