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

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

我所看到的缺点是:

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

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


当前回答

从我所看到的,你应该是一个已经有表设置和数据的数据库。然而,如果你愿意(取决于你拥有的应用程序类型),你可以允许“升级数据库选项”。然后你要做的就是下载最新的sqlite版本,获得在线托管的文本文件的最新Insert/Create语句,执行这些语句,并从旧的db进行数据传输到新的db。

其他回答

如果所需的数据不是很大(限制我不知道,将取决于很多东西),你也可以从网站/网络应用下载数据(XML, JSON,无论什么)。接收到数据后,使用接收到的数据执行SQL语句,创建表并插入数据。

如果你的移动应用程序包含大量数据,稍后用更准确的数据或更改更新已安装的应用程序中的数据可能会更容易。

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); } }

Android已经提供了一种版本感知的数据库管理方法。这种方法已经在Android应用程序的BARACUS框架中得到了利用。

它使您能够在应用程序的整个版本生命周期中管理数据库,能够将sqlite数据库从任何以前的版本更新到当前版本。

此外,它还允许您运行SQLite的热备份和热恢复。

我不是100%确定,但是针对特定设备的热恢复可能使您能够在应用程序中发布准备好的数据库。但我不确定数据库二进制格式,这可能特定于某些设备,供应商或设备代。

因为这些东西是Apache许可证2,所以可以随意重用代码的任何部分,这些代码可以在github上找到

编辑:

如果您只想传送数据,可以考虑在应用程序第一次启动时实例化和持久化pojo。BARACUS有一个内置的支持(内置的键值存储配置信息,例如。"APP_FIRST_RUN"加上一个后上下文引导钩子,以便在上下文上运行后启动操作)。这使得你可以在应用中使用紧密耦合的数据;在大多数情况下,这符合我的用例。

我的解决方案既不使用任何第三方库,也不强迫您在SQLiteOpenHelper子类上调用自定义方法来在创建时初始化数据库。它还负责数据库升级。所有需要做的就是子类化SQLiteOpenHelper。

先决条件:

你希望与应用一起发布的数据库。它应该包含一个名为android_metadata的1x1表,其属性locale值为en_US,此外还有应用特有的表。

子类化SQLiteOpenHelper:

子类SQLiteOpenHelper。 在SQLiteOpenHelper子类中创建一个私有方法。此方法包含将数据库内容从“assets”文件夹中的数据库文件复制到在应用程序包上下文中创建的数据库的逻辑。 重写SQLiteOpenHelper的onCreate, onUpgrade和onOpen方法。

足够的说。下面是SQLiteOpenHelper子类:

public class PlanDetailsSQLiteOpenHelper extends SQLiteOpenHelper {
    private static final String TAG = "SQLiteOpenHelper";

    private final Context context;
    private static final int DATABASE_VERSION = 1;
    private static final String DATABASE_NAME = "my_custom_db";

    private boolean createDb = false, upgradeDb = false;

    public PlanDetailsSQLiteOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    /**
     * Copy packaged database from assets folder to the database created in the
     * application package context.
     * 
     * @param db
     *            The target database in the application package context.
     */
    private void copyDatabaseFromAssets(SQLiteDatabase db) {
        Log.i(TAG, "copyDatabase");
        InputStream myInput = null;
        OutputStream myOutput = null;
        try {
            // Open db packaged as asset as the input stream
            myInput = context.getAssets().open("path/to/shipped/db/file");

            // Open the db in the application package context:
            myOutput = new FileOutputStream(db.getPath());

            // Transfer db file contents:
            byte[] buffer = new byte[1024];
            int length;
            while ((length = myInput.read(buffer)) > 0) {
                myOutput.write(buffer, 0, length);
            }
            myOutput.flush();

            // Set the version of the copied database to the current
            // version:
            SQLiteDatabase copiedDb = context.openOrCreateDatabase(
                DATABASE_NAME, 0, null);
            copiedDb.execSQL("PRAGMA user_version = " + DATABASE_VERSION);
            copiedDb.close();

        } catch (IOException e) {
            e.printStackTrace();
            throw new Error(TAG + " Error copying database");
        } finally {
            // Close the streams
            try {
                if (myOutput != null) {
                    myOutput.close();
                }
                if (myInput != null) {
                    myInput.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                throw new Error(TAG + " Error closing streams");
            }
        }
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.i(TAG, "onCreate db");
        createDb = true;
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.i(TAG, "onUpgrade db");
        upgradeDb = true;
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        Log.i(TAG, "onOpen db");
        if (createDb) {// The db in the application package
            // context is being created.
            // So copy the contents from the db
            // file packaged in the assets
            // folder:
            createDb = false;
            copyDatabaseFromAssets(db);

        }
        if (upgradeDb) {// The db in the application package
            // context is being upgraded from a lower to a higher version.
            upgradeDb = false;
            // Your db upgrade logic here:
        }
    }
}

最后,要获得一个数据库连接,只需在SQLiteOpenHelper子类上调用getReadableDatabase()或getWritableDatabase(),它将负责创建一个db,如果数据库不存在,则从'assets'文件夹中的指定文件复制db内容。

简而言之,您可以使用SQLiteOpenHelper子类来访问资产文件夹中附带的db,就像您在onCreate()方法中使用SQL查询初始化数据库一样。

目前还没有办法预先创建一个SQLite数据库来与apk一起发布。最好的方法是将适当的SQL保存为资源,并从应用程序中运行它们。是的,这会导致数据的重复(同样的信息作为资源和数据库存在),但目前没有其他方法。唯一的缓解因素是apk文件被压缩了。我的经验是908KB压缩到268KB以下。

下面的帖子有我找到的最好的讨论/解决方案,其中有很好的示例代码。

http://groups.google.com/group/android-developers/msg/9f455ae93a1cf152

我把我的CREATE语句存储为一个字符串资源,用Context.getString()读取,并用SQLiteDatabse.execSQL()运行。

我将插入的数据存储在res/raw/inserts中。sql(我创建了sql文件,7000+行)。使用上面链接的技术,我输入了一个循环,逐行读取文件,并将数据连接到“INSERT INTO tbl VALUE”,并执行另一个sqlitedatbase . execsql()。当它们可以连接时,保存7000个“INSERT INTO tbl VALUE”是没有意义的。

在模拟器上大约需要20秒,我不知道在真正的手机上需要多长时间,但它只发生一次,当用户第一次启动应用程序时。