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

Android中SQLite预填充数据库复制不完整问题求助

问题排查与解决方案

看起来你遇到的核心问题是复制后的数据库没有被正确加载,反而系统创建了一个空的数据库(只有默认的ID列,没有你定义的nom列和数据)。这大概率是数据库路径不匹配,以及SQLiteOpenHelper的初始化逻辑导致的,下面一步步拆解原因并给出修复方案:

核心问题分析

  1. 数据库路径错误:你定义的DB_PATH = context.getApplicationInfo().dataDir + "/"并不是Android系统默认存放数据库的路径。SQLiteOpenHelper默认会把数据库放在/data/data/[包名]/databases/目录下,而你复制的数据库到了dataDir根目录,导致SQLiteOpenHelper找不到你复制的文件,自动创建了一个空的数据库。
  2. getReadableDatabase()的副作用:在复制数据库前调用getReadableDatabase()会触发系统自动创建一个空数据库到默认路径,后续你复制的文件并没有覆盖这个空数据库(因为路径不对),所以最终加载的是那个空库。

修复后的完整代码

package com.example.chiffrage;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class DataBaseHelper extends SQLiteOpenHelper {
    private static String DB_NAME = "ChiffrageBDD.db";
    private SQLiteDatabase myDataBase;
    private final Context myContext;

    public DataBaseHelper(Context context) {
        super(context, DB_NAME, null, 10);
        this.myContext = context;
        // 打印默认数据库路径,方便调试
        Log.e("Default DB Path", myContext.getDatabasePath(DB_NAME).getAbsolutePath());
    }

    public void createDataBase() throws IOException {
        boolean dbExist = checkDataBase();
        if (!dbExist) {
            // 关键:不再调用getReadableDatabase(),避免提前创建空数据库
            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying database: " + e.getMessage());
            }
        }
    }

    private boolean checkDataBase() {
        File databasePath = myContext.getDatabasePath(DB_NAME);
        boolean exists = databasePath.exists();
        Log.e("DB Exists", String.valueOf(exists));
        return exists;
    }

    private void copyDataBase() throws IOException {
        InputStream myInput = myContext.getAssets().open(DB_NAME);
        // 使用系统默认的数据库路径作为输出目标
        String outFileName = myContext.getDatabasePath(DB_NAME).getAbsolutePath();
        
        // 确保数据库存放目录存在,避免复制时抛出异常
        File outFile = new File(outFileName);
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }

        OutputStream myOutput = new FileOutputStream(outFileName);
        byte[] buffer = new byte[8192]; // 使用更通用的缓冲区大小
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();
        Log.e("DB Copied", "Successfully copied to: " + outFileName);
    }

    public void openDataBase() throws SQLException {
        String myPath = myContext.getDatabasePath(DB_NAME).getAbsolutePath();
        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) {
            // 升级时先删除旧数据库,再复制新的预填充库
            File dbFile = myContext.getDatabasePath(DB_NAME);
            if (dbFile.exists()) {
                dbFile.delete();
            }
            try {
                copyDataBase();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public List<String> getAllMetal(){
        List<String> returnList = new ArrayList<>();
        // 同时检查表结构和数据,方便调试
        String tableInfoQuery = "PRAGMA table_info(Metal)";
        String dataQuery = "SELECT * FROM Metal";
        
        SQLiteDatabase db = this.getReadableDatabase();
        
        // 打印表结构到日志
        Cursor infoCursor = db.rawQuery(tableInfoQuery, null);
        if (infoCursor.moveToFirst()){
            do {
                String columnName = infoCursor.getString(1);
                Log.e("Column Name", columnName);
                returnList.add(columnName);
            } while (infoCursor.moveToNext());
        }
        infoCursor.close();
        
        // 打印表数据到日志
        Cursor dataCursor = db.rawQuery(dataQuery, null);
        if (dataCursor.moveToFirst()){
            do {
                Log.e("Metal Data", "ID: " + dataCursor.getInt(0) + ", Nom: " + dataCursor.getString(1));
            } while (dataCursor.moveToNext());
        }
        dataCursor.close();
        
        db.close();
        return returnList;
    }
}

关键修改说明

  1. 修正数据库路径:全程使用myContext.getDatabasePath(DB_NAME)获取系统默认的数据库路径,确保复制的数据库和SQLiteOpenHelper加载的是同一个文件。
  2. 移除多余的getReadableDatabase()调用:避免在复制前创建空数据库,直接复制资产中的数据库到正确路径。
  3. 添加目录创建逻辑:确保存放数据库的databases目录存在,防止复制时抛出文件不存在的异常。
  4. 优化onUpgrade逻辑:升级版本时先删除旧数据库,再复制新的预填充数据库,避免版本冲突。
  5. 增强调试日志:添加了路径、数据库存在性、复制结果等日志,方便排查问题。

使用注意事项

  • 确保assets目录下的ChiffrageBDD.db文件没有损坏,且格式是SQLite 3.x版本(Android支持的版本)。
  • 在使用DataBaseHelper时,按以下顺序调用方法:
    try {
        DataBaseHelper dbHelper = new DataBaseHelper(context);
        dbHelper.createDataBase();
        dbHelper.openDataBase();
        List<String> columns = dbHelper.getAllMetal();
        // 处理查询结果
    } catch (IOException e) {
        e.printStackTrace();
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 09:58:12