Android DB覆盖后返回null的游标
我的应用程序有一个静态数据库,我需要用新内容更新它。架构不会更改。我已将DB放入Android DB覆盖后返回null的游标,android,android-listview,listadapter,android-sqlite,android-cursor,Android,Android Listview,Listadapter,Android Sqlite,Android Cursor,我的应用程序有一个静态数据库,我需要用新内容更新它。架构不会更改。我已将DB放入资产/中,当应用程序启动它时,它会将DB(在SQLiteOpenHelper构造函数中)复制到应用程序的数据库/目录中。副本代码如下: private void importAssets(boolean overWrite) throws IOException { AssetManager am = mCtx.getAssets(); InputStream in = am.open(DB_NAME
资产/
中,当应用程序启动它时,它会将DB(在SQLiteOpenHelper
构造函数中)复制到应用程序的数据库/
目录中。副本代码如下:
private void importAssets(boolean overWrite) throws IOException
{
AssetManager am = mCtx.getAssets();
InputStream in = am.open(DB_NAME);
FileOutputStream out;
byte[] buffer = new byte[1024];
int len;
File dir, db;
dir = new File(DB_DIR);
dir.mkdirs();
db = new File(dir, DB_NAME);
// if not first run return
if (!overWrite && db.exists())
{
Log.i(TAG, DB_DIR + "/" + DB_NAME + " exists, do nothing");
in.close();
return;
}
// copy the DB from assets to standard DB dir created above
out = new FileOutputStream(db);
while ((len = in.read(buffer)) > 0)
{
out.write(buffer, 0, len);
}
out.flush();
out.close();
in.close();
if (!db.exists())
{
Log.e(TAG, DB_DIR + "/" + DB_NAME + " does not exist");
}
}
protected void onResume()
{
super.onResume();
mContent = new PocketbookContent(this);
mDB = mContent.getReadableDatabase();
mRows = mDB.query("book_content",
new String[] { "id", "title" },
null,
null,
null,
null,
"id",
null);
String[] chapters = new String[mRows.getCount()];
mRows.moveToFirst();
for (int i = 0; i < mRows.getCount(); i++)
{
// assume title is in column 1
chapters[i] = mRows.getString(1);
mRows.moveToNext();
}
// set the adapter & insert data into view
mAdapter = new ArrayAdapter<String>(this,
R.layout.toc_row, chapters);
setListAdapter(mAdapter);
}
变量overWrite
是一个标志,告诉importasets()
是否覆盖现有数据库。只有在SQLiteOpenHelper.onUpgrade()
中匹配了适当的条件时,才会将其设置为true
。在这种情况下,FileOutputStream
会截断现有文件,并用assets/
中的新文件覆盖它
我这样做的原因是我有很多记录,我不想硬编码到Java源代码中。我只想把数据库放在那里让用户访问。用户无法修改数据库,它仅用于查看
主活动是ListActivity
的子类。它从数据库中读取数据,并使用光标
和列表数组
将其显示在列表中。这在onResume()
中完成,如下所示:
private void importAssets(boolean overWrite) throws IOException
{
AssetManager am = mCtx.getAssets();
InputStream in = am.open(DB_NAME);
FileOutputStream out;
byte[] buffer = new byte[1024];
int len;
File dir, db;
dir = new File(DB_DIR);
dir.mkdirs();
db = new File(dir, DB_NAME);
// if not first run return
if (!overWrite && db.exists())
{
Log.i(TAG, DB_DIR + "/" + DB_NAME + " exists, do nothing");
in.close();
return;
}
// copy the DB from assets to standard DB dir created above
out = new FileOutputStream(db);
while ((len = in.read(buffer)) > 0)
{
out.write(buffer, 0, len);
}
out.flush();
out.close();
in.close();
if (!db.exists())
{
Log.e(TAG, DB_DIR + "/" + DB_NAME + " does not exist");
}
}
protected void onResume()
{
super.onResume();
mContent = new PocketbookContent(this);
mDB = mContent.getReadableDatabase();
mRows = mDB.query("book_content",
new String[] { "id", "title" },
null,
null,
null,
null,
"id",
null);
String[] chapters = new String[mRows.getCount()];
mRows.moveToFirst();
for (int i = 0; i < mRows.getCount(); i++)
{
// assume title is in column 1
chapters[i] = mRows.getString(1);
mRows.moveToNext();
}
// set the adapter & insert data into view
mAdapter = new ArrayAdapter<String>(this,
R.layout.toc_row, chapters);
setListAdapter(mAdapter);
}
为什么我的光标中的第一行为空?我认为DB copy/overwrite没有问题,因为它在新安装时工作正常,升级时复制的DB在使用sqlite3
检查时完好无损
目标API级别为10
编辑:这里是toc_row.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000000"
android:padding="3px"
android:textSize="27dp"
android:textColor="#ffffff" />
注意,TableOfContents
是主要活动,从ListActivity
子类
Edit:使用onUpgrade()方法。它调用importassets()
,并将overWrite
设置为true
public void onUpgrade(SQLiteDatabase db, int oldVer, int newVer)
{
if (ORIG_REL_SCHEMA_VER == oldVer &&
DB_SCHEMA_VER == newVer)
{
try
{
importAssets(true);
}
catch (IOException ioe)
{
Log.e(TAG, "importAssets() exception" + ioe.getMessage());
}
}
}
编辑(发现了一个混乱):我发现了一个解决方法,可以防止崩溃并允许数据库升级。它实际上并没有解决这个问题,但至少对用户来说并不难看。我通过添加一个文件修改了importasets()
。delete()
调用:
if (!overWrite && db.exists())
{
Log.i(TAG, DB_DIR + "/" + DB_NAME + " exists, do nothing");
in.close();
return;
}
else if (overWrite)
{
db.delete();
}
由于只有在从onUpgrade()
调用importasets()
时,overWrite
才为真,因此这只会发生一次。它删除现有数据库并从资产/
复制新数据库。根据我的测试,File.delete()
在对象被销毁之前实际上不会生效,因此事件不会按照代码的建议实际发生。第一次调用TableOfContents.onResume()
时,ArrayAdapter
将显示旧数据,但在下一次调用时,它将显示新数据
无论如何,这是我的难题,因为似乎没有解释为什么Cursor
为第1行获取null
值,而实际上那里有数据
上面输出中显示null的行没有数据。这不是幽灵般的争吵。应显示为“导入通知…”的值
然后,最简单的解决方案是跳过标题为null
的行:
mRows = mDB.query("book_content",
new String[] { "id", "title" },
"WHERE title IS NOT NULL",
null,
null,
null,
"id",
null);
这样就不会使你的应用程序崩溃,你可以保留通知。“问题发生在升级时。”请发布你的onUpgrade()
方法。因此。。。打包在/assets
中的数据库在第一行中没有任何null
值,但是如果调用onUpgrade()
,则null
值出现在第一行,并且现有行仍然存在,但向上推一个索引。assets/
中的DB没有null
值,这是正确的。无论是否调用onUpgrade()
,数据库本身都没有null
值。只是当光标
尝试从数据库读取数据时(在调用onUpgrade()
之后),它会为第一行获得一个null
值(从何处?)。光标
中的所有剩余行都是其应有的状态和位置。光标不会返回任何不存在的行。如果它存在于游标中,则它存在于数据库中。。。您可以使用标题为NULL的删除此行。您还可以更改查询或使用游标适配器跳过任何null
值。上面输出中显示null
的行中没有数据。这不是幽灵般的争吵。应该显示为“导入通知…”的值,所以我不能将其丢弃。尽管我可能会对此进行调查,看看是否发现了任何有用的东西。当我这样做时,它不会崩溃,但数据库的第一行丢失了。这当然是有道理的,因为无论出于何种原因,光标
,都无法读取第一行。