【问题标题】:Java Android copied database is incompleteJava Android 复制的数据库不完整
【发布时间】:2021-06-07 11:42:39
【问题描述】:

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

这里是框架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]” 我应该有一个带有“id”和“nom”的表名“Metal” 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

你的时间。

【问题讨论】:

  • 当您尝试复制填充的数据库时,它位于何处?你想把它复制到哪里?

标签: java android sqlite android-studio sqliteopenhelper


【解决方案1】:

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

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

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

另一个问题是使用this.DB_PATH = context.getApplicationInfo().dataDir + "/"; 的DB_PATH 与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 类,或者您可以复制并使用上面的代码(您显然必须进行一些更改,例如数据库名称)。

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

以下是您可以在 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 表中提取所有行,因此将显示数据库中的所有表。输出将显示在日志中。

【讨论】:

    猜你喜欢
    • 2016-08-21
    • 2013-09-12
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多