如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:
预创建SQLite数据库,并将其包含在。apk? 在应用程序中包含SQL命令,并让它在第一次使用时创建数据库并插入数据?
我所看到的缺点是:
可能的SQLite版本不匹配可能会导致问题,我目前不知道数据库应该去哪里以及如何访问它。 在设备上创建和填充数据库可能需要很长时间。
有什么建议吗?关于任何问题的文档指针将非常感激。
如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:
预创建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克隆。
我修改了类和问题的答案,并编写了一个允许通过DB_VERSION更新数据库的类。
public class DatabaseHelper extends SQLiteOpenHelper {
private static String DB_NAME = "info.db";
private static String DB_PATH = "";
private static final int DB_VERSION = 1;
private SQLiteDatabase mDataBase;
private final Context mContext;
private boolean mNeedUpdate = false;
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
else
DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
this.mContext = context;
copyDataBase();
this.getReadableDatabase();
}
public void updateDataBase() throws IOException {
if (mNeedUpdate) {
File dbFile = new File(DB_PATH + DB_NAME);
if (dbFile.exists())
dbFile.delete();
copyDataBase();
mNeedUpdate = false;
}
}
private boolean checkDataBase() {
File dbFile = new File(DB_PATH + DB_NAME);
return dbFile.exists();
}
private void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
} catch (IOException mIOException) {
throw new Error("ErrorCopyingDataBase");
}
}
}
private void copyDBFile() throws IOException {
InputStream mInput = mContext.getAssets().open(DB_NAME);
//InputStream mInput = mContext.getResources().openRawResource(R.raw.info);
OutputStream mOutput = new FileOutputStream(DB_PATH + DB_NAME);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0)
mOutput.write(mBuffer, 0, mLength);
mOutput.flush();
mOutput.close();
mInput.close();
}
public boolean openDataBase() throws SQLException {
mDataBase = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
return mDataBase != null;
}
@Override
public synchronized void close() {
if (mDataBase != null)
mDataBase.close();
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion)
mNeedUpdate = true;
}
}
使用类。
在活动类中,声明变量。
private DatabaseHelper mDBHelper;
private SQLiteDatabase mDb;
在onCreate方法中,编写以下代码。
mDBHelper = new DatabaseHelper(this);
try {
mDBHelper.updateDataBase();
} catch (IOException mIOException) {
throw new Error("UnableToUpdateDatabase");
}
try {
mDb = mDBHelper.getWritableDatabase();
} catch (SQLException mSQLException) {
throw mSQLException;
}
如果将数据库文件添加到res/raw文件夹中,则使用类的以下修改。
public class DatabaseHelper extends SQLiteOpenHelper {
private static String DB_NAME = "info.db";
private static String DB_PATH = "";
private static final int DB_VERSION = 1;
private SQLiteDatabase mDataBase;
private final Context mContext;
private boolean mNeedUpdate = false;
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
else
DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
this.mContext = context;
copyDataBase();
this.getReadableDatabase();
}
public void updateDataBase() throws IOException {
if (mNeedUpdate) {
File dbFile = new File(DB_PATH + DB_NAME);
if (dbFile.exists())
dbFile.delete();
copyDataBase();
mNeedUpdate = false;
}
}
private boolean checkDataBase() {
File dbFile = new File(DB_PATH + DB_NAME);
return dbFile.exists();
}
private void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
} catch (IOException mIOException) {
throw new Error("ErrorCopyingDataBase");
}
}
}
private void copyDBFile() throws IOException {
//InputStream mInput = mContext.getAssets().open(DB_NAME);
InputStream mInput = mContext.getResources().openRawResource(R.raw.info);
OutputStream mOutput = new FileOutputStream(DB_PATH + DB_NAME);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0)
mOutput.write(mBuffer, 0, mLength);
mOutput.flush();
mOutput.close();
mInput.close();
}
public boolean openDataBase() throws SQLException {
mDataBase = SQLiteDatabase.openDatabase(DB_PATH + DB_NAME, null, SQLiteDatabase.CREATE_IF_NECESSARY);
return mDataBase != null;
}
@Override
public synchronized void close() {
if (mDataBase != null)
mDataBase.close();
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion)
mNeedUpdate = true;
}
}
http://blog.harrix.org/article/6784
我使用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文件中提取数据库文件
我的解决方案既不使用任何第三方库,也不强迫您在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查询初始化数据库一样。
如果所需的数据不是很大(限制我不知道,将取决于很多东西),你也可以从网站/网络应用下载数据(XML, JSON,无论什么)。接收到数据后,使用接收到的数据执行SQL语句,创建表并插入数据。
如果你的移动应用程序包含大量数据,稍后用更准确的数据或更改更新已安装的应用程序中的数据可能会更容易。