Sqlite 数据库在 Samsung Android Oreo 8.0 设备中无法正常运行

Sqlite database is not functioning properly in Samsung Android Oreo 8.0 device

我创建了一个只执行一些数据库读取操作的应用程序,(从数据库中获取记录并执行一些公式并显示结果)

该应用程序在 Lenovo (5.1.1)、Moto(5.1、6.0、7.1.1)、OnePlus (8.1)、Mi A1 (8.0.0) 上测试的所有设备上运行良好, Micromax (5.0), 三星 (7.1)

但三星设备包含 (8.0) 但它不起作用 设备是 Samsung galaxy J8 (SM-J810G)、Samsung galaxy S7 (SM-G930W8)、Samsung galaxy S9 (SM-G960U)。

下面是我使用的代码。

DBQuery.java

package com.test.dbhelper;

import android.content.ContentValues;
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 com.test.common.DBConstants;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class DBAdapter extends SQLiteOpenHelper implements DBConstants {
private static String DB_PATH = "";

private SQLiteDatabase mDB;

private static int DB_VERSION = 1;
private Context appContext;

public DBAdapter(Context context) {
    super(context.getApplicationContext(), DB_NAME, null, DB_VERSION);
    DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
    appContext = context;
    createDataBase();
}

@Override
public void onCreate(SQLiteDatabase db) {
    System.out.println("DB Helper On Create....");
    this.mDB = db;
    createDataBase();
}

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

private boolean checkDataBase() {

    try {
        String myPath = DB_PATH + DB_NAME;
        mDB = SQLiteDatabase.openDatabase(myPath, null,
                SQLiteDatabase.OPEN_READONLY);
    } catch (SQLiteException e) {
    }
    if (mDB != null) {
        mDB.close();
    }
    return mDB != null ? true : false;
}

private void copyDataBase() throws IOException {

    InputStream myInput = appContext.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();
}

public void openDataBase() {
    try {
        String myPath = DB_PATH + DB_NAME;
        mDB = SQLiteDatabase.openDatabase(myPath, null,
                SQLiteDatabase.OPEN_READONLY);
    } catch (Exception e) {
        System.out.println("Open Database failed...");
        e.printStackTrace();
    }
}

public DBAdapter open() throws SQLException {
    mDB = getWritableDatabase();
    return this;
}

@Override
public synchronized void close() {
    if (mDB != null)
        mDB.close();
    super.close();
}

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

}

public int insertData(String table_name, ContentValues initialValues) {
    int record = (int) mDB.insert(table_name, null, initialValues);
    return record;
}

public int deleteData(String table_name, String whereClause) {
    return (int) mDB.delete(table_name, whereClause, null);
}

public int updateData(String table_name, ContentValues initialValues,
                      String whereClause) {
    return mDB.update(table_name, initialValues, whereClause, null);
}

public Cursor executeRawQuery(String query) {
    Cursor c = null;
    c = mDB.rawQuery(query, null);

    return c;
}

}

DBConstants.java

package com.test.common

interface DBConstants {
  companion object {
    const val DB_NAME = "test.db3"
    const val TAB_WELL_RITE = "well_rite"
    const val TAB_WELL_RITE_TANK_VOLUME_IN_GALLON = "tank_volume_in_gallon"
    const val TAB_WELL_RITE_MODEL_WELL_RITE = "model_well_rite"
    const val TAB_WELL_RITE_MODEL_CHALLANGER = "model_challanger"
    const val TAB_WELL_RITE_MODEL_FLEX_LITE = "model_flex_lite"
    const val TAB_WELL_RITE_TANK_VOLUME_IN_LITRE = "tank_volume_in_litre"
  }
}

DBQuery.java

package com.test.dbhelper;

import android.database.Cursor;

import com.test.activity.TestApp;
import com.test.common.Constants;
import com.test.common.DBConstants;
import com.test.common.LogUtils;

import java.util.HashMap;

public class DBQuery implements DBConstants, Constants {

public static String TAG = "DBQuery";

public static HashMap<String, String> getWellRiteModels(String tableName, String field, double value) {

    HashMap<String, String> dataMap = new HashMap<>();

    String query = "SELECT * " + " FROM " + tableName
            + " WHERE " + field + " >= '" + value + "' order by " + field + " LIMIT 1";

    Cursor cursor = TestApp.dbAdapter.executeRawQuery(query);

    if (cursor.getCount() > 0) {
        if (cursor.moveToFirst()) {
            dataMap.put(WELL_RITE, cursor.getString(1));
            dataMap.put(CHALLANGER, cursor.getString(2));
            dataMap.put(FLEX_LITE, cursor.getString(3));
        }
    }
    return dataMap;
  }
}

TestApp.kt

package com.test.activity

import android.app.Application
import com.crashlytics.android.Crashlytics
import com.test.dbhelper.DBAdapter
import io.fabric.sdk.android.Fabric

class TestApp : Application() {

  companion object {
    lateinit var dbAdapter: DBAdapter
  }

  override fun onCreate() {
    super.onCreate()
    Fabric.with(this, Crashlytics())
    dbAdapter = DBAdapter(applicationContext)
    dbAdapter.openDataBase()
  }
}

从 MainActivity.java 我正在调用下面的函数以使用 DBQuery 方法获取数据。

var dataMap: HashMap<kotlin.String, kotlin.String> = HashMap()
dataMap = DBQuery.getWellRiteModels(TAB_WELL_RITE, DBConstants.TAB_WELL_RITE_TANK_VOLUME_IN_GALLON, 32.50)

我有 test.db3 数据库,资产文件夹中有记录。 将从资产复制到 /data/data/... 正如您在 DBAdapter.java class.

中看到的代码

Android 8.0 的三星设备有任何具体问题吗?

下面是错误日志

Fatal Exception: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.test/databases/test.db3
   at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
   at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1742)
   at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1685)
   at com.test.dbhelper.DBAdapter.executeRawQuery(DBAdapter.java:147)
   at com.test.dbhelper.DBQuery.getWellRiteModels(DBQuery.java:26)     

尝试在需要时关闭和打开数据库,截至目前,只需在 checkDataBase() 的代码下方评论它就会像魅力一样工作...

mDB.close();

所以我在 Oreo 中解决了一个类似的问题,该问题来自特定版本的 Sqlite 优化,该版本在其他设备上运行良好,但不适用于 Oreo。如果你有一个复杂的查询,尝试使用交叉连接来强制优化器的一些 tables 的顺序,并在实际查询之前执行“Pragma optimize”。

使用 EXPLAIN QUERY PLAN 显示,在 Oreo 中,优化器在某个时刻扫描了与其他设备不同的 table。这个 table 太大了,所以查询花了很长时间。我无法真正解释为什么这样做,但根据 SQLite documentation ,复杂子查询可能会出现回归。

我不知道它是否对你有帮助,但它对我有用。