如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:

预创建SQLite数据库,并将其包含在。apk? 在应用程序中包含SQL命令,并让它在第一次使用时创建数据库并插入数据?

我所看到的缺点是:

可能的SQLite版本不匹配可能会导致问题,我目前不知道数据库应该去哪里以及如何访问它。 在设备上创建和填充数据库可能需要很长时间。

有什么建议吗?关于任何问题的文档指针将非常感激。


当前回答

2017年11月,谷歌发布了房间持久性库。

从文档中可以看到:

Room持久库在SQLite上提供了一个抽象层 允许流畅的数据库访问,同时利用 SQLite。 该库可以帮助您在设备上创建应用程序数据的缓存 这个缓存,作为你的应用程序的单一 真实源,允许用户查看一致的密钥副本 应用程序中的信息,不管用户是否有 互联网连接。

当第一次创建或打开数据库时,Room数据库具有回调。可以使用create回调填充数据库。

Room.databaseBuilder(context.applicationContext,
        DataDatabase::class.java, "Sample.db")
        // prepopulate the database after onCreate was called
        .addCallback(object : Callback() {
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)
                // moving to a new thread
                ioThread {
                    getInstance(context).dataDao()
                                        .insert(PREPOPULATE_DATA)
                }
            }
        })
        .build()

这篇博文中的代码。

其他回答

我写了一个库来简化这个过程。

dataBase = new DataBase.Builder(context, "myDb").
//        setAssetsPath(). // default "databases"
//        setDatabaseErrorHandler().
//        setCursorFactory().
//        setUpgradeCallback()
//        setVersion(). // default 1
build();

它将从assets/databases/myDb.db文件创建一个dataBase。 此外,你会得到所有这些功能:

从文件加载数据库 同步访问数据库 使用SQLite - Android通过requery, Android特定分发最新版本的SQLite。

从github克隆。

我使用ORMLite和下面的代码为我工作

public class DatabaseProvider extends OrmLiteSqliteOpenHelper {
    private static final String DatabaseName = "DatabaseName";
    private static final int DatabaseVersion = 1;
    private final Context ProvidedContext;

    public DatabaseProvider(Context context) {
        super(context, DatabaseName, null, DatabaseVersion);
        this.ProvidedContext= context;
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        boolean databaseCopied = preferences.getBoolean("DatabaseCopied", false);
        if (databaseCopied) {
            //Do Nothing
        } else {
            CopyDatabase();
            SharedPreferences.Editor editor = preferences.edit();
            editor.putBoolean("DatabaseCopied", true);
            editor.commit();
        }
    }

    private String DatabasePath() {
        return "/data/data/" + ProvidedContext.getPackageName() + "/databases/";
    }

    private void CopyDatabase() {
        try {
            CopyDatabaseInternal();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private File ExtractAssetsZip(String zipFileName) {
        InputStream inputStream;
        ZipInputStream zipInputStream;
        File tempFolder;
        do {
            tempFolder = null;
            tempFolder = new File(ProvidedContext.getCacheDir() + "/extracted-" + System.currentTimeMillis() + "/");
        } while (tempFolder.exists());

        tempFolder.mkdirs();

        try {
            String filename;
            inputStream = ProvidedContext.getAssets().open(zipFileName);
            zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
            ZipEntry zipEntry;
            byte[] buffer = new byte[1024];
            int count;

            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                filename = zipEntry.getName();
                if (zipEntry.isDirectory()) {
                    File fmd = new File(tempFolder.getAbsolutePath() + "/" + filename);
                    fmd.mkdirs();
                    continue;
                }

                FileOutputStream fileOutputStream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + filename);
                while ((count = zipInputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, count);
                }

                fileOutputStream.close();
                zipInputStream.closeEntry();
            }

            zipInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        return tempFolder;
    }

    private void CopyDatabaseInternal() throws IOException {

        File extractedPath = ExtractAssetsZip(DatabaseName + ".zip");
        String databaseFile = "";
        for (File innerFile : extractedPath.listFiles()) {
            databaseFile = innerFile.getAbsolutePath();
            break;
        }
        if (databaseFile == null || databaseFile.length() ==0 )
            throw new RuntimeException("databaseFile is empty");

        InputStream inputStream = new FileInputStream(databaseFile);

        String outFileName = DatabasePath() + DatabaseName;

        File destinationPath = new File(DatabasePath());
        if (!destinationPath.exists())
            destinationPath.mkdirs();

        File destinationFile = new File(outFileName);
        if (!destinationFile.exists())
            destinationFile.createNewFile();

        OutputStream myOutput = new FileOutputStream(outFileName);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }

        myOutput.flush();
        myOutput.close();
        inputStream.close();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int fromVersion, int toVersion) {

    }
}

请注意,该代码从资产中的zip文件中提取数据库文件

将数据库导入apk,然后复制到/data/data/…将使数据库的大小增加一倍(1在apk, 1在data/data/…),并将增加apk的大小(当然)。所以你的数据库不应该太大。

在Android Studio 3.0中附带数据库文件发布应用程序

对我来说,发布带有数据库文件的应用程序是个好主意。这样做的优点是不需要进行复杂的初始化,如果数据集很大,初始化有时会花费大量时间。

步骤1:准备数据库文件

准备好数据库文件。它可以是.db文件或.sqlite文件。如果您使用.sqlite文件,您所需要做的就是更改文件扩展名。步骤是一样的。

在本例中,我准备了一个名为testDB.db的文件。它有一个表和一些样本数据,像这样

步骤2:将文件导入到项目中

如果您还没有资产文件夹,请创建资产文件夹。然后将数据库文件复制并粘贴到此文件夹中

步骤3:复制文件到应用程序的数据文件夹

你需要将数据库文件复制到应用程序的数据文件夹中,以便与它进行进一步的交互。这是复制数据库文件的一次性操作(初始化)。如果多次调用此代码,data文件夹中的数据库文件将被assets文件夹中的数据库文件覆盖。当你想在未来的应用程序更新期间更新数据库时,这个覆盖过程是有用的。

注意,在应用程序更新期间,这个数据库文件不会在应用程序的数据文件夹中被更改。只有卸载才会删除它。

数据库文件需要复制到“/databases”文件夹下。打开设备文件资源管理器。输入data/data/<YourAppName>/ location。这是上面提到的应用程序的默认数据文件夹。默认情况下,数据库文件将放在该目录下的另一个名为databases的文件夹中

现在,复制文件的过程与Java所做的非常相似。使用下面的代码进行复制粘贴。这是起始代码。它还可以用于将来更新(通过覆盖)数据库文件。

//get context by calling "this" in activity or getActivity() in fragment
//call this if API level is lower than 17  String appDataPath = "/data/data/" + context.getPackageName() + "/databases/"
String appDataPath = context.getApplicationInfo().dataDir;

File dbFolder = new File(appDataPath + "/databases");//Make sure the /databases folder exists
dbFolder.mkdir();//This can be called multiple times.

File dbFilePath = new File(appDataPath + "/databases/testDB.db");

try {
    InputStream inputStream = context.getAssets().open("testDB.db");
    OutputStream outputStream = new FileOutputStream(dbFilePath);
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer))>0)
    {
        outputStream.write(buffer, 0, length);
    }
    outputStream.flush();
    outputStream.close();
    inputStream.close();
} catch (IOException e){
    //handle
}

然后刷新文件夹以验证复制过程

步骤4:创建数据库打开帮助程序

为SQLiteOpenHelper创建一个子类,包括连接、关闭、路径等。我把它命名为DatabaseOpenHelper

import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "testDB.db";
    public static final String DB_SUB_PATH = "/databases/" + DB_NAME;
    private static String APP_DATA_PATH = "";
    private SQLiteDatabase dataBase;
    private final Context context;

    public DatabaseOpenHelper(Context context){
        super(context, DB_NAME, null, 1);
        APP_DATA_PATH = context.getApplicationInfo().dataDir;
        this.context = context;
    }

    public boolean openDataBase() throws SQLException{
        String mPath = APP_DATA_PATH + DB_SUB_PATH;
        //Note that this method assumes that the db file is already copied in place
        dataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.OPEN_READWRITE);
        return dataBase != null;
    }

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

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

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

步骤5:创建与数据库交互的顶级类

这将是读取和写入数据库文件的类。还有一个示例查询,用于打印数据库中的值。

import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class Database {
    private final Context context;
    private SQLiteDatabase database;
    private DatabaseOpenHelper dbHelper;

    public Database(Context context){
        this.context = context;
        dbHelper = new DatabaseOpenHelper(context);
    }

    public Database open() throws SQLException
    {
        dbHelper.openDataBase();
        dbHelper.close();
        database = dbHelper.getReadableDatabase();
        return this;
    }

    public void close()
    {
        dbHelper.close();
    }

    public void test(){
        try{
            String query ="SELECT value FROM test1";
            Cursor cursor = database.rawQuery(query, null);
            if (cursor.moveToFirst()){
                do{
                    String value = cursor.getString(0);
                    Log.d("db", value);
                }while (cursor.moveToNext());
            }
            cursor.close();
        } catch (SQLException e) {
            //handle
        }
    }
}

步骤6:测试运行

通过运行以下代码行来测试代码。

Database db = new Database(context);
db.open();
db.test();
db.close();

按下跑步键,欢呼吧!

SQLiteAssetHelper库使得这个任务非常简单。

作为gradle依赖项添加它很容易(但Jar也可用于Ant/Eclipse),它的文档可以在以下地址找到: https://github.com/jgilfelt/android-sqlite-asset-helper

注意:这个项目不再维护如上所述的Github链接。

如文档中所述:

Add the dependency to your module's gradle build file: dependencies { compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+' } Copy the database into the assets directory, in a subdirectory called assets/databases. For instance: assets/databases/my_database.db (Optionally, you may compress the database in a zip file such as assets/databases/my_database.zip. This isn't needed, since the APK is compressed as a whole already.) Create a class, for example: public class MyDatabase extends SQLiteAssetHelper { private static final String DATABASE_NAME = "my_database.db"; private static final int DATABASE_VERSION = 1; public MyDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } }