调用rawQuery()查询Cursor出错:外部SQLite数据库表不存在异常
解决Android中读取外部数据库提示“no such table”的问题
我来帮你排查这个问题!你遇到的SQLiteException: no such table: Calls本质是因为你实际打开的数据库和你复制的不是同一个文件,而且数据库复制的逻辑根本没执行起来。下面一步步给你解决:
问题根源分析
- 路径不匹配:你硬编码的
DB_PATH = "/data/data/com.example.databaseDemo/"并不是SQLiteOpenHelper默认的数据库存储目录(默认是/data/data/包名/databases/),导致你复制的数据库和getReadableDatabase()打开的数据库不是同一个文件。 - 复制逻辑未执行:你的MainActivity只初始化了
DataBaseHelper,但没有调用createDataBase()和openDataBase(),assets里的数据库根本没复制到设备上。 - 错误的数据库实例:
getDataFromDB()里调用this.getReadableDatabase()会创建一个全新的空数据库,自然找不到Calls表。
解决方案步骤
1. 修正数据库存储路径(避免硬编码)
修改DataBaseHelper的路径获取方式,用系统API获取正确的数据库目录:
public class DataBaseHelper extends SQLiteOpenHelper { private static String DB_NAME = "data.db"; private static String DB_PATH; // 去掉硬编码,动态获取 private SQLiteDatabase myDataBase; private final Context myContext; Cursor cursor; public DataBaseHelper(Context context) { super(context, DB_NAME, null, 1); this.myContext = context; // 获取应用专属的数据库目录,适配所有Android版本 DB_PATH = context.getDatabasePath(DB_NAME).getParent() + "/"; } // 删掉你重写的getReadableDatabase()方法,不需要默认的实例 // ... 其他原有方法保持不变 ... }
2. 确保数据库复制和打开逻辑被执行
在MainActivity的onCreate中,先完成数据库的复制和打开操作:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ... RecyclerView初始化代码保持不变 ... helper = new DataBaseHelper(this); try { // 先检查并复制数据库到正确路径 helper.createDataBase(); // 打开我们复制好的数据库 helper.openDataBase(); } catch (IOException e) { e.printStackTrace(); // 这里可以加错误提示,比如Toast } albumList = helper.getDataFromDB(); adapter = new MainAdapter(this, albumList); recyclerView.setAdapter(adapter); adapter.notifyDataSetChanged(); }
3. 使用正确的数据库实例查询数据
修改getDataFromDB()方法,使用我们自己打开的myDataBase,而不是默认的空数据库:
public List<Album> getDataFromDB() { List<Album> modelList = new ArrayList<>(); int i = 1; String query = "select * from Calls where call_key = " + i; // 使用我们复制并打开的myDataBase实例 cursor = myDataBase.rawQuery(query, null); if (cursor.moveToFirst()) { do { Album model = new Album(); model.setName(cursor.getString(2)); model.setNumOfSongs(cursor.getString(3)); modelList.add(model); } while (cursor.moveToNext()); } // 记得关闭Cursor,避免内存泄漏 if (cursor != null && !cursor.isClosed()) { cursor.close(); } Log.d("student data", modelList.toString()); return modelList; }
额外注意事项
- 确认assets目录下的
data.db确实包含Calls表,且表名大小写和代码中一致(SQLite默认大小写不敏感,但最好保持一致)。 - 对于Android 6.0及以上版本,如果你操作的是外部存储需要动态申请权限,但这里是应用内部私有目录,不需要额外权限,Manifest里的权限已经足够。
- 测试前可以先卸载旧应用,避免残留的空数据库影响测试结果。
内容的提问来源于stack exchange,提问作者Rakshit Sorathiya
相关产品推荐
相关产品推荐

