Java Android 复制的数据库不完整

Java Android copied database is incomplete

我正在尝试使用 SQLite 数据库,它是一个已经填满的数据库,经过一些研究我发现我需要复制这个数据库才能使用它,所以我拿起代码并测试了它但是副本不完整,所有列都未复制,数据不存在。当我用 DB Browser for SQLite 打开原始文件时没有问题。

这里是framework databasehelper的代码:

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 android.widget.TextView;

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 {

    String DB_PATH = null;
    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;
        this.DB_PATH = context.getApplicationInfo().dataDir + "/";
        Log.e("Path 1", DB_PATH);
    }


    public void createDataBase() throws IOException {
        boolean dbExist = checkDataBase();
        if (dbExist) {
        } else {
            this.getReadableDatabase();
            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        }
    }

    private boolean checkDataBase() {
//        SQLiteDatabase checkDB = null;
//        try {
//            String myPath = DB_PATH + DB_NAME;
//            checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
//        } catch (SQLiteException e) {
//        }
//        if (checkDB != null) {
//            checkDB.close();
//        }
//        return checkDB != null ? true : false;
        File databasePath = myContext.getDatabasePath(DB_NAME);
        return databasePath.exists();
    }

    private void copyDataBase() throws IOException {
        InputStream myInput = myContext.getAssets().open(DB_NAME);
        String outFileName = DB_PATH + DB_NAME;
        OutputStream myOutput = new FileOutputStream(outFileName);
        byte[] buffer = new byte[2068];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();

    }

    public void openDataBase() throws SQLException {
        String myPath = DB_PATH + DB_NAME;
        myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    }

    @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)
            try {
                copyDataBase();
            } catch (IOException e) {
                e.printStackTrace();

            }
    }

    public List getAllMetal(){
        List returnList = new ArrayList();
        String queryString = "PRAGMA table_info(Metal)";
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.rawQuery(queryString, null);
        if (cursor.moveToFirst()){
            do {
                returnList.add(cursor.getString(1));
            } while (cursor.moveToNext());
            } else {
            //failure
        }
        cursor.close();
        db.close();
        return returnList;
    }
}

getAllMetal 的响应是“[ID]” 我应该有一个 table 名称“Metal”,带有“id”和“nom” nom 是金属的名称。 没有数据,我使用 PRAGMA 验证该列,如您所见,缺少“nom”列。

这里是我获得代码的一些资源:

[框架 SQLiteOpenHelper] https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper#SQLiteOpenHelper(android.content.Context,%20java.lang.String,%20android.database.sqlite.SQLiteDatabase.CursorFactory,%20int)

[复制数据库] Getting "Failed to open database" error when copying a sqlite database from assets In Android 4.2

时间不多了。

如果 table 不存在,使用 PRAGMA table_info(Metal) 不会失败。相反,它不会显示任何行。因此它可能会产生误导,也可能会误导您。

它显示的是数据库本身存在。但是,仅仅因为数据库存在并不意味着副本本身有效。由于您只显示数据库助手,而不显示实例的用途,因此可能存在各种问题。

我怀疑您遇到了问题并且无意中创建了一个空的数据库(就用户定义的 tables 而言)。这将允许您调用 getAllMetal 并获得您所说的结果。

另一个问题是 DB_PATH 根据使用 this.DB_PATH = context.getApplicationInfo().dataDir + "/"; 将与 File databasePath = myContext.getDatabasePath(DB_NAME); 不同。他们将是

  • /data/data//ChiffrageBDD.db
  • /data/data//databases/ChiffrageBDD.db
    • 反映使用的包名称

使用 Android 很容易意外创建空数据库,因此我怀疑空数据库的存在是您的问题。一旦创建,数据库就会持续存在(除非您专门删除它,否则它始终存在)这也可能是问题的一部分。

不知道你迄今为止所做的一切,也不知道你如何使用 DataBaseHelper 实例,这只是对原因的猜测。

您的 DataBaseHelper 确实存在问题,因为它不适合全新安装的应用程序。安装应用程序时,/data/data/ 目录存在,但数据库目录不存在。 copyDataBase 方法将失败,因为数据库目录不存在。

这是一个可用的数据库助手,您可能希望比较它与您正在使用的工具之间的差异。它们很微妙但很重要:-

public class DBAssetHelper2 extends SQLiteOpenHelper {

    static String DATABASE_NAME = "databasefile2";

    public DBAssetHelper2(Context context) {
        super(context, DATABASE_NAME, null, 1);
        String databasePath = context.getDatabasePath(DATABASE_NAME).getPath();
        if (!doesDatabaseExist(databasePath)) {
            copyDatabaseFromAssets(context,databasePath,DATABASE_NAME);
        }
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

    // Checks to see if the database exists, if it does not
    // then checks to see if the directories in the path exist
    // and then makes the directories if they do not
    private boolean doesDatabaseExist(String databasepath) {
        if (new File(databasepath).exists()) return true;
        if (new File(databasepath).getParentFile().exists()) return false;
        new File(databasepath).getParentFile().mkdirs();
        return false;
    }

    private void copyDatabaseFromAssets(Context context, String databasepath, String assetfilename) {
        int bSize = 4096, bytes = 0;
        byte[] buffer = new byte[bSize];
        try {
            InputStream asset = context.getAssets().open(assetfilename);
            FileOutputStream database = new FileOutputStream(new File(databasepath));
            while((bytes = asset.read(buffer)) > 0) {
                database.write(buffer,0,bytes);
            }
            database.flush();
            database.close();
            asset.close();

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Error copying Asset File " + assetfilename + " to " + databasepath);
        }
    }
}

首先,当您创建助手的实例时,它会检查数据库,如果需要,还会检查资产的副本。也就是说,无需调用任何方法来复制数据库(使用您的助手,您需要调用 createDataBase 方法)。

如果您还查看 doesDatabaseExist 方法,您会发现如果数据库文件不存在,则会进一步检查路径中的目录是否存在,如果不存在,则会丢失目录。

异常处理是不可或缺的,如果复制失败并出现 运行 时间异常。

您可能希望相应地修改您的 DataBaseHelper class 或者您可以复制并使用上面的代码(您显然必须进行一些更改,例如数据库名称)。

重要 完成建议的更改后,您必须 删除现有的(可能是空的)数据库。最简单的方法是卸载应用程序,然后 运行 应用程序。

以下是您可以在 Activity 中使用上述内容的示例:-

public class MainActivity extends AppCompatActivity {

    DBAssetHelper2 dbAssetHelper2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        dbAssetHelper2 = new DBAssetHelper2(this);
        Cursor cursor2 = dbAssetHelper2.getReadableDatabase().query("sqlite_master",null,null,null,null,null,null);
        DatabaseUtils.dumpCursor(cursor2);
        cursor2.close();
    }
}

请注意,实例化后的代码从 sqlite_master table 中提取所有行,因此将显示数据库中的所有 table。输出将显示在日志中。