You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 9(API 28)从文件路径填充数据库失败,提示表不存在

Android 9 (API 28) 复制Assets数据库后找不到表的问题解决

问题背景

采用Assets目录复制数据库的方案在Android 8.1及以下版本正常运行,但在Android 9(API 28)中启动Exammode Activity时触发SQLiteException,提示找不到Technical表,执行SELECT Id FROM Technical WHERE TopicId ='Che1'语句失败。

复制数据库代码

private static String DB_NAME = "QuotesData.db";
private static String DB_PATH = "/data/data/[PACKAGE NAME]/databases/";
private void copyDataBase() throws IOException {

    InputStream myInput = context.getAssets().open(DB_NAME);

    String outFileName = DB_PATH + DB_NAME;

    OutputStream myOutput = new FileOutputStream(outFileName);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }

    myOutput.flush();
    myOutput.close();
    myInput.close();

}

报错日志

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.in22labs.neet, PID: 23832
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.in22labs.neet/com.in22labs.neet.Quiz.Exammode}: android.database.sqlite.SQLiteException: no such table: Technical (code 1 SQLITE_ERROR): , while compiling: SELECT Id FROM Technical WHERE TopicId ='Che1'
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

解决方案

1. 替换硬编码的数据库路径

Android 9及以上版本不推荐硬编码/data/data/[PACKAGE]/databases/路径,改用系统API获取正确路径,避免权限或路径识别问题:

private static String DB_NAME = "QuotesData.db";

// 获取正确的数据库路径
private String getDBPath() {
    return context.getDatabasePath(DB_NAME).getAbsolutePath();
}

private void copyDataBase() throws IOException {
    InputStream myInput = context.getAssets().open(DB_NAME);
    String outFileName = getDBPath();
    
    // 确保数据库父目录存在
    File dbDir = new File(outFileName).getParentFile();
    if (!dbDir.exists()) {
        dbDir.mkdirs();
    }

    OutputStream myOutput = new FileOutputStream(outFileName);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }

    myOutput.flush();
    myOutput.close();
    myInput.close();
}

2. 禁用SQLite WAL模式

Android 9默认启用WAL(Write-Ahead Logging)模式,复制数据库时若未同步WAL文件会导致数据库不完整。在打开数据库后执行语句禁用WAL:

// 在DataBaseTechnical类的初始化或打开数据库逻辑中添加
SQLiteDatabase db = getWritableDatabase();
db.execSQL("PRAGMA journal_mode=DELETE;");

3. 复制前清理旧数据库文件

若之前存在不完整的数据库副本,会导致加载异常,复制前先删除旧文件:

private void copyDataBase() throws IOException {
    String outFileName = getDBPath();
    File oldDbFile = new File(outFileName);
    if (oldDbFile.exists()) {
        oldDbFile.delete();
    }

    // 后续复制逻辑...
    InputStream myInput = context.getAssets().open(DB_NAME);
    OutputStream myOutput = new FileOutputStream(outFileName);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }

    myOutput.flush();
    myOutput.close();
    myInput.close();
}

4. 验证数据库复制完整性

复制完成后检查表是否存在,确保复制过程无异常:

// 复制后添加校验逻辑
private boolean isDatabaseValid() {
    String dbPath = getDBPath();
    SQLiteDatabase db = null;
    Cursor cursor = null;
    try {
        db = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY);
        cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='Technical'", null);
        return cursor.getCount() > 0;
    } catch (SQLiteException e) {
        e.printStackTrace();
        return false;
    } finally {
        if (cursor != null) cursor.close();
        if (db != null) db.close();
    }
}

调用copyDataBase()后可通过isDatabaseValid()验证,若返回false则重新触发复制流程。


内容的提问来源于stack exchange,提问作者Shakthi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:32:15