SQLiteDiskIOException:磁盘I / O错误(代码3850)

时间:2015-09-24 13:49:31

标签: android sqlite android-sqlite

我在某些设备上遇到上述错误(很少见,直到现在只有2次):

android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 3850)
    at android.database.sqlite.SQLiteConnection.nativeExecuteForString(Native Method)
    at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:679)
    at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:361)
    at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:236)
    at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:200)
    at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
    at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
    at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
    at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:806)
    at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:791)
    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
    at ***.a(***.java:115)

我的代码中的第115行如下:

// here the exception occurs
SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY); 
// ...
db.close();

故事

我的工作如下:

  • app具有root权限
  • 它将数据库从另一个应用程序复制到它自己的目录
  • 它尝试打开此数据库并读取一些数据
  • 它再次关闭文件

这就是全部。这适用于数千种设备。我的应用程序肯定只能在现场访问数据库,我完全确定

问题

有谁知道什么可能导致这个问题?

也许有趣的事实

  • 这2个设备是OnePlus2
  • 一个人告诉我,更新到Oxygen 2.1后发生问题

3 个答案:

答案 0 :(得分:1)

我最近遇到了一个类似的问题,我的应用程序在所有Android设备上运行良好,除了运行OxygenOS 2.1的OnePlus Two。

在查看问题之后,看起来这个特定组合非常敏感,并且在某种类型的数据库锁上崩溃。就我而言,我的应用程序是在第一次运行时将新数据库复制到设备,然后检查数据库版本以查看是否需要更新。检查时它在这些设备上崩溃了。我在我的代码中意识到,我在打开一个单独的实例到数据库,而不是在检查版本时使用当前打开的数据库。在更改代码以避免打开数据库的单独实例后,应用程序停止在OnePlus上崩溃。

我的建议是不要在你的应用程序中打开数据库的多个实例,或者在再次打开数据库之前先尝试关闭数据库(也许你的复制方法没有关闭数据库或正在使用同一数据库的另一个实例)

答案 1 :(得分:1)

刚刚在开发中遇到过这个问题。它在电话电量耗尽并关机时触发。据推测,当发生这种情况时,你只需要抓住数据库锁。

答案 2 :(得分:0)

这是工作解决方案(已经在生产中测试了2个月左右),这也适用于OnePlus2和Android 6:

然后在您的应用中,尝试阅读如下的外部数据库:

  • 找出哪个二进制文件正在运行。我尝试了一些方法(例如通过调用/proc/cpuinfo)但找不到可靠的解决方案,所以我通过试验和错误来完成,如下所示:
    • 首先,我将二进制文件从我的assets文件夹复制到我的应用程序files文件夹
    • 然后我尝试通过<path_to_SQLite3_bnary> <path_to_database> \.dump '<tablename>'\n转储所需的数据库并阅读结果
    • 我检查结果,如果它包含error:我知道二进制文件不起作用,如果它以INSERT INTO开头我知道它有效
    • 我用二进制文件重复此操作,直到找到工作二进制文件
  • 然后我就读了结果。结果将包含错误或某些csv类似的内容。我要么处理错误,要么通过row.replace("INSERT INTO \"" + tablename + "\" VALUES(", "").replace(");", "")将每一行转换为有效的csv,以便我以csv格式获取内容。我使用opencsv的CSVReader解析数据然后......

就是这样,这对所有设备都有效(直到知道)没有问题

代码 - 从我的来源复制,采用它以满足您的需求

public static List<String> readCSVData(String pathDatabase, String tableName) throws InterruptedException, TimeoutException, RootDeniedException, IOException 
{
    List<String> res = null;
    List<String> csvData = null;

    final String[] architectures = new String[]{
            "armv7",
            "armv7-pie",
            "armv6",
            "armv6-nofpu"
    };

    for (int i = 0; i < architectures.length; i++)
    {
        res = readDatabase(architectures[i], pathDatabase, tableName, rootMethod);

        if (res.toString().contains("[error:"))
        {
            L.d(RootNetworkUtil.class, "Trial and Error - ERROR: " + architectures[i]);
        }
        else
        {
            int maxLength = (res.toString().length() < 100) ? res.toString().length() : 100;
            L.d(RootNetworkUtil.class, "Trial and Error - RESULT: " + res.toString().substring(0, maxLength));
            L.d(RootNetworkUtil.class, "Architecture found via trial and error: " + architectures[i]);
            csvData = res;
            break;
        }
    }

    return csvData;
}

private static List<String> readDatabase(String architecture, String pathDB, String tablename) throws InterruptedException, TimeoutException, RootDeniedException, IOException {
    String sqlite = "sqlite3." + architecture;
    String pathSQLite3 = getSQLitePath(architecture, tablename);
    // OWN class, just copy the file from the assets to the sqlite3 path!!! 
    AssetUtil.copyAsset(sqlite, pathSQLite3);

    String[] cmd = new String[]{
            "su\n",
            //"chown root.root " + pathSQLite3 + "\n",
            "chmod 777 " + pathSQLite3 + "\n",
            pathSQLite3 + " " + pathDB + " \".dump '" + tablename + "'\"\n"
    };
    List<String> res = new ArrayList<>();
    List<String> temp = RootUtils.execute(cmd);
    for (int i = 0; i < temp.size(); i++)
    {
        // Fehlerzeilen behalten!!!
        if (temp.get(i).contains("error:"))
            res.add(temp.get(i));
        else if (temp.get(i).startsWith("INSERT INTO \"" + tablename + "\""))
            res.add(temp.get(i).replace("INSERT INTO \"" + tablename + "\" VALUES(", "").replace(");", ""));
    }
    return res;
}

public static String getSQLitePath(String architecture, String addon)
{
    String sqlite = "sqlite3." + architecture;
    String pathSQLite3 = "/data/data/" + MainApp.get().getPackageName() + "/files/" + sqlite + addon;
    return pathSQLite3;
}