我在Mac上使用了sqlite3来创建一个包含一个表的小型数据库文件(姓氏" .db"),以及包含" en_US"的android_metadata表。我将文件粘贴到Android Studio项目的assets文件夹中。在运行时,我的SQLiteOpenHelper的onCreate方法将文件复制到我的Genymotion模拟器(Samsung Galaxy S5)上的/data/data/projectname/databases/filename.db中。但是当我尝试使用SQLiteOpenHelper的getReadableDatabase方法提供的Cursor读取filename.db中的表时,我得到了LogCat错误" E / SQLiteLog:(1)没有这样的表:tablename"。同样的事情发生在我的el-cheapo Azpen A727 Android平板电脑上。
复制到模拟器上/ data / data / projectname / databases文件夹中的filename.db文件包含与我在Mac上使用sqlite3创建的原始filename.db相同的字节数(5120)。当我用Unix八进制转储命令" od -c"比较两个.db文件时,我发现前16个字节它们是相同的,并且第17个字节第一次不同。 (原始文件的八进制004为第17个字节,副本为020。)这两个文件的内容是否相同?当我尝试使用sqlite3 .dump检查/data/data/projectname/databases/filename.db时,我得到"错误:(11)数据库磁盘映像格式错误"。
我的SQLiteOpenHelper的getReadableDatabase方法触发对helper的onCreate方法的调用。在onCreate中,我将filename.db从assets文件夹复制到/ data / data / projectname / databases目录。在onCreate结束时,我验证了filename.db的两个副本中的第17个字节是004。
我在我的SQLiteOpenHelper中添加了一个onOpen方法。它在onCreate之后由getReadableDatabase自动调用,除了调用super.onOpen之外什么都不做。在调用super.onOpen之后,我的onOpen打印了filename.db的两个副本的第17个字节。资产中的字节仍为004,但/ data / data / projectname / databases中的字节已变为020.(如果onOpen不调用super.onOpen,也会发生损坏。)
在对onCreate的调用结束和对onOpen的调用开始之间,有什么险恶的力量可能会损坏/data/data/projectname/databases/filename.db?或者问题完全是另一回事?非常感谢你。
package edu.nyu.scps.offer;
import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Helper extends SQLiteOpenHelper {
private static final String DB_NAME = "offers.db";
private Context context;
public Helper(Context context) {
super(context, context.getDatabasePath(DB_NAME).getPath(), null, 1);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
try {
AssetManager assetManager = context.getAssets();
InputStream inputStream = assetManager.open(DB_NAME); //in assets folder
OutputStream outputStream = new FileOutputStream(getDatabaseName());
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.flush();
outputStream.close();
inputStream.close();
} catch (IOException exception) {
Log.e("myTag", "exception " + exception);
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
};
这是我使用sqlite3手动创建的文件的.dump:
sqlite3 offers.db .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE android_metadata (
"locale" text default "en_US"
);
INSERT INTO "android_metadata" VALUES('en_US');
CREATE TABLE offers (
_id integer primary key autoincrement,
title text,
description text
);
INSERT INTO "offers" VALUES(1,'Prewar Castle','It reminds me of the broken battlements of my own castle in Transylvania.');
INSERT INTO "offers" VALUES(2,'Shelter Island','heated saltwater pool, tennis, professional landscaping, finished lower level.');
INSERT INTO "offers" VALUES(3,'Amagansett','Heated pool, room for tennis, adjacent to nature conservancy, provate beach and pier rights.');
INSERT INTO "offers" VALUES(4,'Sagaponack Village','Insulate yourself from the barbarians at the hedgerow.');
INSERT INTO "offers" VALUES(5,'Amagansett Dunes','Close proximity to ocean beaches, oversized hot tub, 2 fireplaces. 4 bedrooms, 2.5 baths, .17 acres.');
INSERT INTO "offers" VALUES(6,'East Hampton Village','Outdoor shower, 2 car garage, fireplace, close to village and ocean beaches.');
INSERT INTO "offers" VALUES(7,'Yonkers','Batcave with stunning views of ice drifting down the Hudson. Hiking on Old Croton Aqueduct. Floods when Saw Mill River rises.');
INSERT INTO "offers" VALUES(8,'Tarrytown','Listen to the music of the New York State Thruway.');
INSERT INTO "offers" VALUES(9,'East Village','Pay extortion for a shoebox!');
INSERT INTO "offers" VALUES(10,'Poughkeepsie','Ranch-style living in chip plant recently vacated by a shrinking IBM.');
DELETE FROM sqlite_sequence;
INSERT INTO "sqlite_sequence" VALUES('offers',10);
COMMIT;
2015年4月15日:我发现为什么我的.db文件已损坏。当类SQLiteOpenHelper的getReadableDatabase方法调用onCreate时,.db文件已经创建,甚至在数据库事务中。我的onCreate中的代码完全覆盖了这个文件。稍后SQLiteOpenHelper尝试完成事务,但是用于启动事务的.db文件已被销毁。解决方案:在调用 getReadableDatabase之前,必须从项目的资产中复制.db文件。最直接的方法是将它复制到我的SQLiteOpenHelper子类的构造函数中。但Android的文档说SQLiteOpenHelper的构造函数必须“快速返回”#34;并将.db文件的创建推迟到getReadableDatabase。所以我最终用一个复制.db文件的新方法覆盖了getReadableDatabase,然后调用了超类SQLiteOpenHelper的getReadableDatabase。如果发现该文件已存在,super.getReadableDatabase将不会尝试创建新的.db文件。细节省略。 我调试原始代码只是因为我很想知道为什么它损坏了.db文件。在现实生活中,我将使用SQLiteAssetHelper,我很感激这个建议。