无法在某些Android设备中正确复制SQLite数据库

时间:2018-08-17 13:32:01

标签: android sqlite

我已经开发了一个使用SQLite数据库的应用程序。它将在全新安装时复制到应用程序的数据文件夹中,并且每次更新应用程序时(由于数据库是只读的,因此我会覆盖最新版本)。

这是我正在使用的代码:

public class IfcDatabase extends SQLiteOpenHelper {

    private static final Object lock = new Object();
    private static volatile IfcDatabase mInstance;
    private SQLiteDatabase db = null;
    private Context ctxt;

    public static final int DATABASE_VERSION = 22;
    public static final String DATABASE_NAME = "mydatabase.sqlite";
    public static String DB_PATH = "";

    public static IfcDatabase getInstance(Context ctx) {

        IfcDatabase r = mInstance;
        if (r == null) {
            synchronized (lock)
            {    // While we were waiting for the lock, another
                r = mInstance;        // thread may have instantiated the object.
                if (r == null)
                {
                    DB_PATH = ctx.getDatabasePath(DATABASE_NAME).getParent()+"/";
                    Log.e("logs", "DB_PATH="+DB_PATH);
                    r = new IfcDatabase(ctx.getApplicationContext());
                    mInstance = r;
                }
            }
        }
        return r;
    }


    private IfcDatabase(Context context) {
        //super(context, DATABASE_NAME, null, DATABASE_VERSION);
        super(context, DB_PATH+DATABASE_NAME, null, DATABASE_VERSION);
        ctxt = context;
        try {
            if (!existsDatabase()) {
                SQLiteDatabase tempDb = getWritableDatabase();
                Log.e("logs", "Just before copying database");
                copyDatabase();
                Log.e("logs", "Just after copying database");
            }
        } catch (SQLException eSQL) {
            Log.e("logs", "Error: Can not open database");
        } catch (IOException IOe) {
            Log.e("logs", "Error: Can not copy initial database");
        }
    }

    private boolean existsDatabase() {
        File dbFile = new File(DB_PATH + DATABASE_NAME);
        return dbFile.exists();
    }

    public void copyDatabase() throws IOException {
        AssetManager _asset = ctxt.getAssets();
        InputStream myInput = _asset.open(DATABASE_NAME);
        String outFileName = DB_PATH + DATABASE_NAME;
        Log.e("logs", "OUTFILENAME="+outFileName);
        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();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

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

    public void openDatabase() {
        db = getWritableDatabase();
        Log.e("logs", "Checking if table exists (checking if database has been successfully copied)");
        if(!checkIfTableExists("mytable"))
            Log.e("logs", "At this moment this table doesn't exist");
        else
            Log.e("logs", "At this moment this table exists");
        Log.e("logs", "OPEN BBDD "+db);
    }

    private boolean checkIfTableExists(String table_name)
    {
        Cursor _cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='"+table_name+"'", null);
        int count = _cursor.getCount();
        _cursor.close();
        if(count > 0)
            return true;
        else
            return false;
    }

    public void closeDatabase() {
        if (db != null)
            db.close();
    }

    ...

}

启动应用程序以及用户登录时调用:

IfcDatabase.getInstance(this).openDatabase();

并且只有在用户注销时它才调用:

IfcDatabase.getInstance(this).closeDatabase();

在全新安装中,将调用openDatabase,将数据库从资产复制到数据文件夹,并且用户可以开始对数据库进行查询。这些是日志:

DB_PATH=/data/user/0/com.mypackage.myapp/databases/
Just before copying database
OUTFILENAME=/data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite
Just after copying database
Checking if table exists (checking if database has been successfully copied)
At this moment this table exists
OPEN BBDD SQLiteDatabase: /data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite

好吧,这几乎在所有情况下都发生了(这可以正常工作),但是在某些特定的Android机型(Google Pixel和某些ZTE)中却发生了。在这些情况下,似乎此代码无法正确复制数据库。在这些情况下,在全新安装中是以下日志:

DB_PATH=/data/user/0/com.mypackage.myapp/databases/
Just before copying database
OUTFILENAME=/data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite
Just after copying database
Checking if table exists (checking if database has been successfully copied)
At this moment this table doesn't exist

如您所见,数据库似乎为空或不存在。当用户进行查询时,应用程序会因以下异常而崩溃:

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite

那么,Google Pixel和ZTE是否有可能以不同的方式使用SQLite数据库,而我的代码在这种情况下不起作用?

非常感谢您!

---编辑---

我应该补充一点,我一直在考虑这是否可能是权限问题。我请求“写入外部存储”权限,如果用户拒绝它,我要求确认,并且如果用户一直拒绝它,则该用户将无法使用该应用程序。但我几乎可以确定这与我的问题无关。

---新编辑---

我发现了一个新事物:似乎这不仅是Google Pixel问题,而且是带有Android P的Google Pixel。这让我非常担心,因为如果是与Android P相关的问题,它将在所有设备中发生。未来。

目前,我没有可以安装Android P且无法阅读Android P规范的设备,我看不到任何与数据库或内部文件相关的信息或类似信息。 有谁知道Android P是否更改了与数据库,资产,数据文件夹等相关的任何内容??

1 个答案:

答案 0 :(得分:1)

也许您可以通过将其添加到SQLiteOpenHelper

来解决它
@Override
public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    db.disableWriteAheadLogging();
}