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

Android SQLiteDiskIOException:磁盘I/O错误(代码3850),android,sqlite,android-sqlite,Android,Sqlite,Android Sqlite,我在某些设备上遇到上述错误(非常罕见,到目前为止只有2次): 我的代码中的第115行如下所示: // here the exception occurs SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY); // ... db.close(); 故事 我所做的是: 应用程序具有根权限 它将数据库从另一个应用程序复制到自己的目录中 它试图打开此数据库并读取一些

我在某些设备上遇到上述错误(非常罕见,到目前为止只有2次):

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

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

我所做的是:

  • 应用程序具有根权限
  • 它将数据库从另一个应用程序复制到自己的目录中
  • 它试图打开此数据库并读取一些数据
  • 它会再次关闭文件
就这些。这在成千上万的设备上工作。我的应用程序肯定只访问on place的数据库,对此我完全肯定

问题

有人知道是什么导致了这个问题吗

可能是有趣的事实

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;
}
  • 这两个设备是一个OnePlus2
  • 一个人告诉我,问题发生在更新到氧气2.1之后

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

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


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

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

  • sqlite
    二进制文件添加到您的应用程序中(可在此处找到:)。只需将它们放入您的应用程序资产文件夹中
然后在应用程序中,尝试读取外部数据库,如下所示:

// here the exception occurs
SQLiteDatabase db = SQLiteDatabase.openDatabase(pathApp, null, SQLiteDatabase.OPEN_READONLY); 
// ...
db.close();
  • 找出哪个二进制文件在工作。我尝试了几种方法(例如,通过调用
    /proc/cpuinfo
    ),但找不到可靠的解决方案,因此我通过以下反复试验来实现:
    • 首先,我将二进制文件从我的资产文件夹复制到我的应用程序
      文件
      文件夹
    • 然后我尝试通过
      \.dump'\n
      转储所需的数据库并读取结果
    • 我检查结果,如果它包含
      错误:
      我知道二进制文件不工作,如果它以
      插入
      开始,我知道它工作
    • 我用我的二进制文件重复这个过程,直到找到一个可用的二进制文件
  • 然后我就看了结果。结果将包含错误或类似内容。我要么处理错误,要么通过
    行将每一行转换为有效的csv。replace(“插入\”+tablename+“\”值(“,”)。replace(“);”,”)
    ,以便以
    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;
}
publicstaticlist readCSVData(stringpathdatabase,stringtablename)抛出InterruptedException、TimeoutException、RootDeniedException、IOException
{
列表res=null;
列表csvData=null;
最终字符串[]体系结构=新字符串[]{
“armv7”,
“armv7派”,
“armv6”,
“armv6 nofpu”
};
for(int i=0;i
在开发过程中遇到了这种情况。当手机电池耗尽并关机时,它会触发。大概,发生这种情况时,你只需要抓取数据库锁。

我突然感到