在未root的设备上从assets文件夹复制数据库

21

我正在尝试将数据库从assets文件夹复制到设备上。这段代码在模拟器和已root设备上都能正常工作。我想知道在未root设备上是否会出现任何问题,或者它会同样工作。

private void StoreDatabase() {
    File DbFile = new File(
            "data/data/packagename/DBname.sqlite");
    if (DbFile.exists()) {
        System.out.println("file already exist ,No need to Create");
    } else {
        try {
            DbFile.createNewFile();
            System.out.println("File Created successfully");
            InputStream is = this.getAssets().open("DBname.sqlite");
            FileOutputStream fos = new FileOutputStream(DbFile);
            byte[] buffer = new byte[1024];
            int length = 0;
            while ((length = is.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }
            System.out.println("File succesfully placed on sdcard");
            // Close the streams
            fos.flush();
            fos.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

1
是的,您的代码片段在未root的设备上也完美运行 :) - Amit Anand
6个回答

18

这将在所有设备和模拟器上确保正常工作,无需root。

/**
 * Copies your database from your local assets-folder to the just created
 * empty database in the system folder, from where it can be accessed and
 * handled. This is done by transfering bytestream.
 * */
private void copyDataBase(String dbname) throws IOException {
    // Open your local db as the input stream
    InputStream myInput = myContext.getAssets().open(dbname);
    // Path to the just created empty db
    File outFileName = myContext.getDatabasePath(dbname);
    // Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(outFileName);
    // transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }
    // Close the streams
    myOutput.flush();
    myOutput.close();
    myInput.close();
}

我的代码也能正常工作,我只是想知道它是否会在未经root的设备上出现问题? - Krishnakant Dalal
15
永远不要硬编码路径。在许多Android环境中(例如辅助帐户),outFileName的值将是错误的。使用getDatabasePath()来获取数据库文件的路径。 - CommonsWare
感谢@CommonsWare。我将把它改为String outFileName = getDatabasePath() + dbname; - abbas.aniefa
3
这句话的意思是:这不应该是myContext.getDatabasePath(dbname)吗?此外,getDatabasePath(dbName)返回一个文件对象而不是字符串。 - Andrew S
这似乎会导致导出一个加密的数据库文件(请参见问题 - https://stackoverflow.com/questions/44397083/android-copy-and-export-application-db-results-in-encrypted-db-on-unrooted-ph) - Garbit

6
    /**
 * Copy database file from assets folder inside the apk to the system database path.
 * @param context Context
 * @param databaseName Database file name inside assets folder
 * @param overwrite True to rewrite on the database if exists
 * @return True if the database have copied successfully or if the database already exists without overwrite, false otherwise.
 */
private boolean copyDatabaseFromAssets(Context context, String databaseName , boolean overwrite)  {

    File outputFile = context.getDatabasePath(databaseName);
    if (outputFile.exists() && !overwrite) {
        return true;
    }

    outputFile = context.getDatabasePath(databaseName + ".temp");
    outputFile.getParentFile().mkdirs();

    try {
        InputStream inputStream = context.getAssets().open(databaseName);
        OutputStream outputStream = new FileOutputStream(outputFile);


        // transfer bytes from the input stream into the output stream
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, length);
        }

        // Close the streams
        outputStream.flush();
        outputStream.close();
        inputStream.close();

        outputFile.renameTo(context.getDatabasePath(databaseName));

    } catch (IOException e) {
        if (outputFile.exists()) {
            outputFile.delete();
        }
        return false;
    }

    return true;
}

1
我给了一个+1,因为你的代码中context对象的getDatabasePath()方法和outputFile.getParentFile().mkdirs()的组合解决了我的问题。在其他示例中使用硬编码目录路径时,数据库复制失败。 - Casey Perkins

1

我不确定,但这在我测试过的每个设备上都有效。我从某个地方借鉴了这种方法,并将其通用化,既可用于备份又可用于恢复:

public static void movedb(File srcdb, File destdb)
{
    try 
    {
        if (Environment.getExternalStorageDirectory().canWrite()) 
        {                 
            if (srcdb.exists()) 
            {
                FileChannel src = new FileInputStream(srcdb).getChannel();
                FileChannel dst = new FileOutputStream(destdb).getChannel();
                dst.transferFrom(src, 0, src.size());
                src.close();
                dst.close();                    
            }
            else
            {
                //ERROR: "Database file references are incorrect"                    
            }
        }
        else
        {
           //ERROR: "Cannot write to file"
        }
    }
    catch (Exception e) 
    {
        //ERROR: e.getMessage()
    }
}

然后我只需调用备份:

movedb(this, getDatabasePath(getDbName()), new File(Environment.getExternalStorageDirectory(), getDatabaseBackupPath()));

getDatabasePath()getDatabaseBackupPath()只是字符串值

。这与IT相关。

我的代码也能正常工作,我只是想知道它是否会在未经root的设备上出现问题? - Krishnakant Dalal

0
private void copyDataBase(Context context) throws IOException {

    //Log.i(TAG, "Opening Asset...");
    // Open your local db as the input stream
    InputStream myInput = context.getAssets().open(DBHelper.DATABASE_NAME);

   // Log.i(TAG, "Getting db path...");
    // Path to the just created empty db
    File dbFile = getDatabasePath(DBHelper.DATABASE_NAME);

    if (!dbFile.exists()) {
        SQLiteDatabase checkDB = context.openOrCreateDatabase(DBHelper.DATABASE_NAME, context.MODE_PRIVATE, null);
        if (checkDB != null) {
            checkDB.close();
        }
    }

    //Log.i(TAG, "Getting output stream...");
    // Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(dbFile);

  //  Log.i(TAG, "Writing data...");
    // transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }
    // Close the streams
    myOutput.flush();
    myOutput.close();
    myInput.close();
}

0

这适用于 Kotlin。

assets.open("sqlite_db_in_assets.db")
      .copyTo(getDatabasePath("sqlite_db_in_device.db").outputStream())

-1
虽然从技术上来说是可行的,但我并不认为复制(无论是从还是到)可能是活动数据库文件是一个好主意。

这可能更适合作为一条注释。您认为它值得成为一个答案的原因是什么? - Dharman
@Dharman 因为如果正确,它可以证明关闭整个问题并将其标记为“不良实践”的做法是合理的。不确定这里是如何工作的... - Srg
这里的工作方式是,除非直接回答问题,否则我们会在问题下面发布评论。如果这是一种不好的做法,你应该警告他们,但他们可能仍然希望得到问题的答案。 - Dharman
你看,这是我正在寻找答案的一个实际问题。对我来说,这似乎是一个有着糟糕答案的糟糕问题。我不是一个管理员,所以关闭东西不是我的职责,但这也不仅仅是我在这里发表评论。但请随意投票。 - Srg
好的,那我来给您提些建议。如果您想将这个作为答案,则需要详细解释一下为什么认为这样做不是一个好主意。目前您的回答似乎只是个人观点而没有任何支撑。 - Dharman
1
好的 :) 一旦我理解了wal_checkpoint,我会回来详细说明。如果我有时间的话。在那之前,我提供25年的系统管理员和编程经验直觉。读者可以接受或放弃。 - Srg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接