【问题标题】:SQLiteDiskIOException: disk I/O error (code 3850)SQLiteDiskIOException:磁盘 I/O 错误(代码 3850)
【发布时间】:2016-04-25 22:50:36
【问题描述】:

我在某些设备上遇到上述错误(非常罕见,到目前为止只有 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();

故事

我所做的如下:

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

就是这样。这适用于数千台设备。我的应用程序肯定只在原地访问数据库,我对此完全确定

问题

有谁知道是什么导致了这个问题?

可能是有趣的事实

  • 这 2 台设备是 OnePlus2
  • 有人告诉我更新到 Oxygen 2.1 后出现问题

【问题讨论】:

  • 我也突然收到有关此错误的用户报告(自 9 月 25 日以来;在此之前它从未出现过)。所有实例都在一台设备上并运行 Android 5.1.1。也许该设备收到了导致此问题的最新系统更新?
  • 我所知道的两个受影响的设备确实获得了系统更新...到 Oxygen 2.1...但我对此一无所知...而且还没有找到任何东西...
  • 终于找到了解决方案:-)。将 sqlite 二进制文件添加到您的应用程序中,找出哪个在设备上运行,然后将数据库转储到控制台并解析输出......这甚至在 OnePlus2 上也能正常工作
  • 令人惊讶的是我在 OnePlus2 上遇到了同样的问题
  • @prom85,你是怎么做到的......你能详细说明你的答案吗......(我们可以通过聊天继续)

标签: android sqlite android-sqlite


【解决方案1】:

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

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

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

【讨论】:

  • 我的复制方法将文件复制为流,因此任何内部标志都将保持原样...复制文件后如何关闭数据库?我正在从另一个应用程序复制一个文件(我的应用程序具有 root 权限)并将其复制到我的目录中,以便我可以使用 SQLiteDatabase...
  • 在我的应用程序中,我也将其复制为流,但这发生在同一个应用程序和另一个函数中,该函数首先尝试打开数据库以检查它是否存在,然后关闭它然后复制它作为流。因此,请确保在复制之前将其关闭。
  • 我不能...我无法控制数据库文件... Maxbe 我可以通过 sqlite3 以某种方式直接关闭它...我必须检查...
  • 不要照原样复制它,而是尝试将原始数据库转储为 sql 文件并将其作为新数据库文件导入您的应用程序,然后将其复制过来 - 这样您就可以获得它的新副本,而无需担心标志和其他问题。
  • 对于 android 6,无论如何我都必须将 sqlite3 二进制文件添加到我的应用程序中并使用它......我也试过了,但目前(仍在等待进一步的反馈)这在这个特定问题上失败了设备也是如此......如果转储成功,我会尝试,然后尝试读取转储的文件
【解决方案2】:

刚在开发中遇到这个。它在手机没电并关机时触发。据推测,当这种情况发生时,您只需要抓住数据库锁。

【讨论】:

    【解决方案3】:

    这是可行的解决方案(现在已经在生产环境中测试了 2 个月左右),它也适用于 OnePlus2 和 Android 6:

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

    • 找出正在运行的二进制文件。我尝试了一些方法(例如通过调用/proc/cpuinfo)但找不到可靠的解决方案,所以我通过反复试验来做到这一点,如下所示:
      • 首先我将二进制文件从资产文件夹复制到我的应用程序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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-27
      相关资源
      最近更新 更多