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()
//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
}
package farhangsarasIntroduction;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DataBaseHelper extends SQLiteOpenHelper{
//The Android's default system path of your application database.
private static String DB_PATH = "data/data/com.example.sample/databases";
private static String DB_NAME = "farhangsaraDb";
private SQLiteDatabase myDataBase;
private final Context myContext;
/**
* Constructor
* Takes and keeps a reference of the passed context in order to access to the application assets and resources.
* @param context
*/
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
/**
* Creates a empty database on the system and rewrites it with your own database.
* */
public void createDataBase() {
boolean dbExist;
try {
dbExist = checkDataBase();
} catch (SQLiteException e) {
e.printStackTrace();
throw new Error("database dose not exist");
}
if(dbExist){
//do nothing - database already exist
}else{
try {
copyDataBase();
} catch (IOException e) {
e.printStackTrace();
throw new Error("Error copying database");
}
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
}
}
/**
* Check if the database already exist to avoid re-copying the file each time you open the application.
* @return true if it exists, false if it doesn't
*/
private boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH +"/"+ DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
throw new Error("database does't exist yet.");
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
/**
* 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() throws IOException{
//copyDataBase();
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH +"/"+ DB_NAME;
File databaseFile = new File( DB_PATH);
// check if databases folder exists, if not create one and its subfolders
if (!databaseFile.exists()){
databaseFile.mkdir();
}
//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();
}
@Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
you to create adapters for your views.
}
There are two options for creating and updating databases.
一种是在外部创建数据库,然后将其放在项目的assets文件夹中,然后从那里复制整个数据库 . 如果数据库有很多表和其他组件,这会快得多 . Upgrades are triggered by changing the database version number in the res/values/strings.xml file. 然后通过外部创建新数据库,用新数据库替换assets文件夹中的旧数据库,将旧数据库保存在另一个名称的内部存储中,将新数据库从assets文件夹复制到内部存储,转移,即可完成升级 . 旧数据库(先前已重命名)中的所有数据都进入新数据库,最后删除旧数据库 . 您最初可以使用 SQLite Manager FireFox plugin 创建数据库来执行创建sql语句 .
另一种选择是从sql文件内部创建数据库 . 这不是那么快,但如果数据库只有几个表,则延迟可能对用户来说是不明显的 . Upgrades are triggered by changing the database version number in the res/values/strings.xml file. 然后通过处理升级sql文件来完成升级 . 数据库中的数据将保持不变,除非删除其容器,例如删除表 .
The example below demonstrates how to use either method.
这是一个示例create_database.sql文件 . 它将被放置在项目的assets文件夹中以用于内部方法,或者复制到SQLite Manager的“Execute SQL”中以创建外部方法的数据库 . (NOTE: Notice the comment about the table required by Android.)
--Android requires a table named 'android_metadata' with a 'locale' column
CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US');
INSERT INTO "android_metadata" VALUES ('en_US');
CREATE TABLE "kitchen_table";
CREATE TABLE "coffee_table";
CREATE TABLE "pool_table";
CREATE TABLE "dining_room_table";
CREATE TABLE "card_table";
这里有一个示例update_database.sql文件 . 它将被放置在项目的assets文件夹中,用于内部方法,或者复制到SQLite Manager的“Execute SQL”中,以便为外部方法创建数据库 . (NOTE: Notice that all three types of SQL comments will be ignored by the sql parser that is included in this example.)
--CREATE TABLE "kitchen_table"; This is one type of comment in sql. It is ignored by parseSql.
/*
* CREATE TABLE "coffee_table"; This is a second type of comment in sql. It is ignored by parseSql.
*/
{
CREATE TABLE "pool_table"; This is a third type of comment in sql. It is ignored by parseSql.
}
/* CREATE TABLE "dining_room_table"; This is a second type of comment in sql. It is ignored by parseSql. */
{ CREATE TABLE "card_table"; This is a third type of comment in sql. It is ignored by parseSql. }
--DROP TABLE "picnic_table"; Uncomment this if picnic table was previously created and now is being replaced.
CREATE TABLE "picnic_table" ("plates" TEXT);
INSERT INTO "picnic_table" VALUES ('paper');
这是一个访问数据库然后使用它的活动 . ( Note: You might want to run the database code in a separate thread if it uses a lot of resources. )
package android.example;
import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
/**
* @author Danny Remington - MacroSolve
*
* Activity for demonstrating how to use a sqlite database.
*/
public class Database extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
DatabaseHelper myDbHelper;
SQLiteDatabase myDb = null;
myDbHelper = new DatabaseHelper(this);
/*
* Database must be initialized before it can be used. This will ensure
* that the database exists and is the current version.
*/
myDbHelper.initializeDataBase();
try {
// A reference to the database can be obtained after initialization.
myDb = myDbHelper.getWritableDatabase();
/*
* Place code to use database here.
*/
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
myDbHelper.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
myDb.close();
}
}
}
}
这是数据库助手类,必要时可以在其中创建或更新数据库 . (NOTE: Android requires that you create a class that extends SQLiteOpenHelper in order to work with a Sqlite database.)
package android.example;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* @author Danny Remington - MacroSolve
*
* Helper class for sqlite database.
*/
public class DatabaseHelper extends SQLiteOpenHelper {
/*
* The Android's default system path of the application database in internal
* storage. The package of the application is part of the path of the
* directory.
*/
private static String DB_DIR = "/data/data/android.example/databases/";
private static String DB_NAME = "database.sqlite";
private static String DB_PATH = DB_DIR + DB_NAME;
private static String OLD_DB_PATH = DB_DIR + "old_" + DB_NAME;
private final Context myContext;
private boolean createDatabase = false;
private boolean upgradeDatabase = false;
/**
* Constructor Takes and keeps a reference of the passed context in order to
* access to the application assets and resources.
*
* @param context
*/
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, context.getResources().getInteger(
R.string.databaseVersion));
myContext = context;
// Get the path of the database that is based on the context.
DB_PATH = myContext.getDatabasePath(DB_NAME).getAbsolutePath();
}
/**
* Upgrade the database in internal storage if it exists but is not current.
* Create a new empty database in internal storage if it does not exist.
*/
public void initializeDataBase() {
/*
* Creates or updates the database in internal storage if it is needed
* before opening the database. In all cases opening the database copies
* the database in internal storage to the cache.
*/
getWritableDatabase();
if (createDatabase) {
/*
* If the database is created by the copy method, then the creation
* code needs to go here. This method consists of copying the new
* database from assets into internal storage and then caching it.
*/
try {
/*
* Write over the empty data that was created in internal
* storage with the one in assets and then cache it.
*/
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
} else if (upgradeDatabase) {
/*
* If the database is upgraded by the copy and reload method, then
* the upgrade code needs to go here. This method consists of
* renaming the old database in internal storage, create an empty
* new database in internal storage, copying the database from
* assets to the new database in internal storage, caching the new
* database from internal storage, loading the data from the old
* database into the new database in the cache and then deleting the
* old database from internal storage.
*/
try {
FileHelper.copyFile(DB_PATH, OLD_DB_PATH);
copyDataBase();
SQLiteDatabase old_db = SQLiteDatabase.openDatabase(OLD_DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase new_db = SQLiteDatabase.openDatabase(DB_PATH,null, SQLiteDatabase.OPEN_READWRITE);
/*
* Add code to load data into the new database from the old
* database and then delete the old database from internal
* storage after all data has been transferred.
*/
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* 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() throws IOException {
/*
* Close SQLiteOpenHelper so it will commit the created empty database
* to internal storage.
*/
close();
/*
* Open the database in the assets folder as the input stream.
*/
InputStream myInput = myContext.getAssets().open(DB_NAME);
/*
* Open the empty db in interal storage as the output stream.
*/
OutputStream myOutput = new FileOutputStream(DB_PATH);
/*
* Copy over the empty db in internal storage with the database in the
* assets folder.
*/
FileHelper.copyFile(myInput, myOutput);
/*
* Access the copied database so SQLiteHelper will cache it and mark it
* as created.
*/
getWritableDatabase().close();
}
/*
* This is where the creation of tables and the initial population of the
* tables should happen, if a database is being created from scratch instead
* of being copied from the application package assets. Copying a database
* from the application package assets to internal storage inside this
* method will result in a corrupted database.
* <P>
* NOTE: This method is normally only called when a database has not already
* been created. When the database has been copied, then this method is
* called the first time a reference to the database is retrieved after the
* database is copied since the database last cached by SQLiteOpenHelper is
* different than the database in internal storage.
*/
@Override
public void onCreate(SQLiteDatabase db) {
/*
* Signal that a new database needs to be copied. The copy process must
* be performed after the database in the cache has been closed causing
* it to be committed to internal storage. Otherwise the database in
* internal storage will not have the same creation timestamp as the one
* in the cache causing the database in internal storage to be marked as
* corrupted.
*/
createDatabase = true;
/*
* This will create by reading a sql file and executing the commands in
* it.
*/
// try {
// InputStream is = myContext.getResources().getAssets().open(
// "create_database.sql");
//
// String[] statements = FileHelper.parseSqlFile(is);
//
// for (String statement : statements) {
// db.execSQL(statement);
// }
// } catch (Exception ex) {
// ex.printStackTrace();
// }
}
/**
* Called only if version number was changed and the database has already
* been created. Copying a database from the application package assets to
* the internal data system inside this method will result in a corrupted
* database in the internal data system.
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
/*
* Signal that the database needs to be upgraded for the copy method of
* creation. The copy process must be performed after the database has
* been opened or the database will be corrupted.
*/
upgradeDatabase = true;
/*
* Code to update the database via execution of sql statements goes
* here.
*/
/*
* This will upgrade by reading a sql file and executing the commands in
* it.
*/
// try {
// InputStream is = myContext.getResources().getAssets().open(
// "upgrade_database.sql");
//
// String[] statements = FileHelper.parseSqlFile(is);
//
// for (String statement : statements) {
// db.execSQL(statement);
// }
// } catch (Exception ex) {
// ex.printStackTrace();
// }
}
/**
* Called everytime the database is opened by getReadableDatabase or
* getWritableDatabase. This is called after onCreate or onUpgrade is
* called.
*/
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
/*
* Add your public helper methods to access and get content from the
* database. You could return cursors by doing
* "return myDataBase.query(....)" so it'd be easy to you to create adapters
* for your views.
*/
}
这是FileHelper类,它包含字节流复制文件和解析sql文件的方法 .
package android.example;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.channels.FileChannel;
/**
* @author Danny Remington - MacroSolve
*
* Helper class for common tasks using files.
*
*/
public class FileHelper {
/**
* Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
* of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
* it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
* path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
* <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
* operation.
*
* @param fromFile
* - InputStream for the file to copy from.
* @param toFile
* - InputStream for the file to copy to.
*/
public static void copyFile(InputStream fromFile, OutputStream toFile) throws IOException {
// transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
try {
while ((length = fromFile.read(buffer)) > 0) {
toFile.write(buffer, 0, length);
}
}
// Close the streams
finally {
try {
if (toFile != null) {
try {
toFile.flush();
} finally {
toFile.close();
}
}
} finally {
if (fromFile != null) {
fromFile.close();
}
}
}
}
/**
* Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
* of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
* it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
* path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
* <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
* operation.
*
* @param fromFile
* - String specifying the path of the file to copy from.
* @param toFile
* - String specifying the path of the file to copy to.
*/
public static void copyFile(String fromFile, String toFile) throws IOException {
copyFile(new FileInputStream(fromFile), new FileOutputStream(toFile));
}
/**
* Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
* of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
* it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
* path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
* <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
* operation.
*
* @param fromFile
* - File for the file to copy from.
* @param toFile
* - File for the file to copy to.
*/
public static void copyFile(File fromFile, File toFile) throws IOException {
copyFile(new FileInputStream(fromFile), new FileOutputStream(toFile));
}
/**
* Creates the specified <i><b>toFile</b></i> that is a byte for byte a copy
* of <i><b>fromFile</b></i>. If <i><b>toFile</b></i> already existed, then
* it will be replaced with a copy of <i><b>fromFile</b></i>. The name and
* path of <i><b>toFile</b></i> will be that of <i><b>toFile</b></i>. Both
* <i><b>fromFile</b></i> and <i><b>toFile</b></i> will be closed by this
* operation.
*
* @param fromFile
* - FileInputStream for the file to copy from.
* @param toFile
* - FileInputStream for the file to copy to.
*/
public static void copyFile(FileInputStream fromFile, FileOutputStream toFile) throws IOException {
FileChannel fromChannel = fromFile.getChannel();
FileChannel toChannel = toFile.getChannel();
try {
fromChannel.transferTo(0, fromChannel.size(), toChannel);
} finally {
try {
if (fromChannel != null) {
fromChannel.close();
}
} finally {
if (toChannel != null) {
toChannel.close();
}
}
}
}
/**
* Parses a file containing sql statements into a String array that contains
* only the sql statements. Comments and white spaces in the file are not
* parsed into the String array. Note the file must not contained malformed
* comments and all sql statements must end with a semi-colon ";" in order
* for the file to be parsed correctly. The sql statements in the String
* array will not end with a semi-colon ";".
*
* @param sqlFile
* - String containing the path for the file that contains sql
* statements.
*
* @return String array containing the sql statements.
*/
public static String[] parseSqlFile(String sqlFile) throws IOException {
return parseSqlFile(new BufferedReader(new FileReader(sqlFile)));
}
/**
* Parses a file containing sql statements into a String array that contains
* only the sql statements. Comments and white spaces in the file are not
* parsed into the String array. Note the file must not contained malformed
* comments and all sql statements must end with a semi-colon ";" in order
* for the file to be parsed correctly. The sql statements in the String
* array will not end with a semi-colon ";".
*
* @param sqlFile
* - InputStream for the file that contains sql statements.
*
* @return String array containing the sql statements.
*/
public static String[] parseSqlFile(InputStream sqlFile) throws IOException {
return parseSqlFile(new BufferedReader(new InputStreamReader(sqlFile)));
}
/**
* Parses a file containing sql statements into a String array that contains
* only the sql statements. Comments and white spaces in the file are not
* parsed into the String array. Note the file must not contained malformed
* comments and all sql statements must end with a semi-colon ";" in order
* for the file to be parsed correctly. The sql statements in the String
* array will not end with a semi-colon ";".
*
* @param sqlFile
* - Reader for the file that contains sql statements.
*
* @return String array containing the sql statements.
*/
public static String[] parseSqlFile(Reader sqlFile) throws IOException {
return parseSqlFile(new BufferedReader(sqlFile));
}
/**
* Parses a file containing sql statements into a String array that contains
* only the sql statements. Comments and white spaces in the file are not
* parsed into the String array. Note the file must not contained malformed
* comments and all sql statements must end with a semi-colon ";" in order
* for the file to be parsed correctly. The sql statements in the String
* array will not end with a semi-colon ";".
*
* @param sqlFile
* - BufferedReader for the file that contains sql statements.
*
* @return String array containing the sql statements.
*/
public static String[] parseSqlFile(BufferedReader sqlFile) throws IOException {
String line;
StringBuilder sql = new StringBuilder();
String multiLineComment = null;
while ((line = sqlFile.readLine()) != null) {
line = line.trim();
// Check for start of multi-line comment
if (multiLineComment == null) {
// Check for first multi-line comment type
if (line.startsWith("/*")) {
if (!line.endsWith("}")) {
multiLineComment = "/*";
}
// Check for second multi-line comment type
} else if (line.startsWith("{")) {
if (!line.endsWith("}")) {
multiLineComment = "{";
}
// Append line if line is not empty or a single line comment
} else if (!line.startsWith("--") && !line.equals("")) {
sql.append(line);
} // Check for matching end comment
} else if (multiLineComment.equals("/*")) {
if (line.endsWith("*/")) {
multiLineComment = null;
}
// Check for matching end comment
} else if (multiLineComment.equals("{")) {
if (line.endsWith("}")) {
multiLineComment = null;
}
}
}
sqlFile.close();
return sql.toString().split(";");
}
}
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:
}
}
}
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);
}
}
16 回答
Android已经提供了一种版本感知的数据库管理方法 . 这种方法已经在针对Android应用程序的BARACUS框架中得到了充分利用 .
It enables you to manage the database along the entire version lifecycle of an app, beeing able to update the sqlite database from any prior version to the current one.
Also, it allows you to run hot-backups and hot-recovery of the SQLite.
我不是100%肯定,但 specific 设备的热恢复可以让您在应用程序中发送准备好的数据库 . 但我不确定数据库二进制格式可能特定于某些设备,供应商或设备代 .
由于这些东西是Apache License 2,所以可以随意重用代码的任何部分,which can be found on github
编辑:
如果您只想发送数据,可以考虑首先启动应用程序中的POJO实例化和持久化 . BARACUS得到了内置的支持(配置信息的内置键值存储,例如“APP_FIRST_RUN”加上后上下文引导挂钩,以便在上下文中运行启动后操作) . 这使您可以在应用程序附带紧密耦合的数据;在大多数情况下,这适合我的用例 .
我刚刚在一篇名为Using your own SQLite database in Android applications的文章中找到了在
ReignDesign blog
中做到这一点的方法 . 基本上你预先创建你的数据库,把它放在你的apk资产目录中,并首先使用复制到/data/data/YOUR_PACKAGE/databases/
目录 .我写了一个library来简化这个过程 .
它将从
assets/databases/myDb.db
文件创建一个dataBase . 此外,您将获得所有这些功能:从文件加载数据库
同步访问数据库
使用sqlite-android通过requery,Android特定分发最新版本的SQLite .
从github克隆它 .
2017年11月谷歌发布了Room Persistence Library
从文档:
首次创建或打开数据库时,Room数据库具有回调功能 . 您可以使用create callback来填充数据库 .
代码来自blog post .
在Android Studio 3.0中将应用程序与数据库文件一起发送
使用数据库文件运送应用程序对我来说是一个好主意 . 优点是您不需要进行复杂的初始化,如果您的数据集很大,有时会花费大量时间 .
Step 1: Prepare database file
准备好您的数据库文件 . 它可以是.db文件或.sqlite文件 . 如果使用.sqlite文件,则只需更改文件扩展名即可 . 步骤是一样的 .
在这个例子中,我准备了一个名为testDB.db的文件 . 它有一个表和一些样本数据就像这样
Step 2: Import the file into your project
如果您还没有,请创建资产文件夹 . 然后将数据库文件复制并粘贴到此文件夹中
Step 3: Copy the file to the app's data folder
您需要将数据库文件复制到应用程序的数据文件夹,以便与其进行进一步的交互 . 这是复制数据库文件的一次性操作(初始化) . 如果多次调用此代码,数据文件夹中的数据库文件将被assets文件夹中的数据库文件覆盖 . 当您希望在将来更新应用程序期间更新数据库时,此覆盖过程非常有用 .
请注意,在应用更新期间,不会在应用的数据文件夹中更改此数据库文件 . 只有卸载才能将其删除 .
需要将数据库文件复制到
/databases
文件夹 . 打开Device File Explorer . 输入data/data/<YourAppName>/
位置 . 这是上面提到的应用程序的默认数据文件夹 . 默认情况下,数据库文件将放在此目录下名为databases的另一个文件夹中现在,复制文件进程与Java正在做的非常相似 . 使用以下代码执行复制粘贴 . 这是启动代码 . 它还可以用于将来更新(通过覆盖)数据库文件 .
然后刷新文件夹以验证复制过程
Step 4: Create database open helper
为
SQLiteOpenHelper
创建一个子类,包括connect,close,path等 . 我把它命名为DatabaseOpenHelper
Step 5: Create top level class to interact with the database
这将是读写数据库文件的类 . 还有一个示例查询可以打印出数据库中的值 .
Step 6: Test running
通过运行以下代码行来测试代码 .
点击运行按钮并加油!
我想直到今天最好和最新的方式是使用
SQLiteAssetHelper
类 .This tutorial通过在Android中导入和使用外部数据库完美指导您
import android.content.Context;
import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
公共类DatabaseOpenHelper扩展SQLiteAssetHelper {
private static final String DATABASE_NAME =“quotes.db”;
private static final int DATABASE_VERSION = 1;
public DatabaseOpenHelper(Context context){
super(context,DATABASE_NAME,null,DATABASE_VERSION);
}
请注意,DatabaseOpenHelper不是扩展SQLiteOpenHelper,而是扩展SQLiteAssetHelper类 .
步骤9:创建一个新类DatabaseAccess并输入如下所示的代码 . 有关此课程的更多详细信息,请参阅高级Android数据库教程 . package com.javahelps.externaldatabasedemo;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
公共类DatabaseAccess {
private SQLiteOpenHelper openHelper;
私有SQLiteDatabase数据库;
private static DatabaseAccess实例;
/ **
*私有构造函数,以避免从外部类创建对象 .
*
private DatabaseAccess(Context context){
this.openHelper = new DatabaseOpenHelper(context);
}
/ **
*返回DatabaseAccess的单例实例 .
*
public static DatabaseAccess getInstance(Context context){
if(instance == null){
instance = new DatabaseAccess(context);
}
返回实例;
}
/ **
*打开数据库连接 .
public void open()
/ **
*关闭数据库连接 .
public void close(){
if(database!= null){
this.database.close();
}
}
/ **
*阅读数据库中的所有报价 .
*
public List <String> getQuotes(){
List <String> list = new ArrayList <>();
Cursor cursor = database.rawQuery(“SELECT * FROM quotes”,null);
cursor.moveToFirst();
while(!cursor.isAfterLast()){
list.add(cursor.getString(0));
cursor.moveToNext();
}
cursor.close();
返回清单;
}
在这个类中,只实现
getQuotes
方法来从数据库中读取数据 . 你有充分的自由插入,像往常一样更新和删除数据库中的所有行 . 有关更多详细信息,请单击此链接高级Android数据库 . 所有与数据库相关的设置都已完成,现在我们需要创建一个ListView来显示引号 . 第10步:在activity_main.xml中添加ListView . <FrameLayout xmlns:android =“http://schemas.android.com/apk/res/android”
的xmlns:工具= “http://schemas.android.com/tools”
机器人:layout_width = “match_parent”
机器人:layout_height = “match_parent”
机器人:paddingBottom来= “@扪/ activity_vertical_margin”
机器人:paddingLeft = “@扪/ activity_horizontal_margin”
机器人:paddingRight = “@扪/ activity_horizontal_margin”
机器人:paddingTop = “@扪/ activity_vertical_margin”
工具:上下文= “MainActivity”>
<ListView的
android:id =“@ id / listView”
机器人:layout_width = “match_parent”
机器人:layout_height = “match_parent”
android:layout_gravity =“center”/>
</的FrameLayout>
步骤11:在MainActivity的onCreate方法中找到ListView的对象,并提供从数据库中读取的引号 . 包com.javahelps.externaldatabasedemo;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.List;
公共类MainActivity扩展ActionBarActivity {
私有ListView listView;
@覆盖
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
的setContentView(R.layout.activity_main);
this.listView =(ListView)findViewById(R.id.listView);
DatabaseAccess databaseAccess = DatabaseAccess.getInstance(this);
databaseAccess.open();
List <String> quotes = databaseAccess.getQuotes();
databaseAccess.close();
ArrayAdapter <String> adapter = new ArrayAdapter <String>(this,android.R.layout.simple_list_item_1,
引号);
this.listView.setAdapter(适配器);
}
}
步骤12:保存所有更改并运行应用程序 .
除了这篇文章,您还可以下载
SQLiteAssetHelper
here最后我做到了!!我使用过这个链接help Using your own SQLite database in Android applications,但不得不稍微改变一下 .
private static String DB_PATH = "data/data/masterPakageName/databases";
我改变了将数据库从本地文件夹复制到模拟器文件夹的方法!当该文件夹不存在时,它有一些问题 . 首先,它应该检查路径,如果它不存在,它应该创建文件夹 .
在前面的代码中,当数据库不存在且
checkDataBase
方法导致异常时,从未调用copyDatabase
方法 . 所以我稍微更改了代码 .如果您的数据库没有文件扩展名,请不要使用文件名 .
它对我很好,我希望它对你也有用
目前没有办法预先创建一个与您的apk一起提供的SQLite数据库 . 您可以做的最好的事情是将适当的SQL保存为资源并从您的应用程序中运行它们 . 是的,这会导致数据重复(相同的信息存在于resrouce和数据库中),但现在没有别的办法了 . 唯一的缓解因素是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”并执行另一个SQLiteDatabase.execSQL() . 当他们可以简单地保持7000“INSERT INTO tbl VALUE”时没有任何意义 .
在模拟器上大约需要20秒钟,我不知道这对真正的手机需要多长时间,但只有当用户首次启动应用程序时才会发生一次 .
在apk中运送数据库然后将其复制到
/data/data/...
将使数据库的大小加倍(apk中为1,data/data/...
中为1),并且将增加apk大小(当然) . 所以你的数据库不应该太大 .如果所需的数据不是太大(限制我不知道,取决于很多事情),您也可以从网站/ webapp下载数据(XML,JSON等) . 接收后,使用接收的数据执行SQL语句,创建表并插入数据 .
如果您的移动应用程序包含大量数据,则稍后可能更容易使用更准确的数据或更改来更新已安装应用程序中的数据 .
There are two options for creating and updating databases.
一种是在外部创建数据库,然后将其放在项目的assets文件夹中,然后从那里复制整个数据库 . 如果数据库有很多表和其他组件,这会快得多 . Upgrades are triggered by changing the database version number in the res/values/strings.xml file. 然后通过外部创建新数据库,用新数据库替换assets文件夹中的旧数据库,将旧数据库保存在另一个名称的内部存储中,将新数据库从assets文件夹复制到内部存储,转移,即可完成升级 . 旧数据库(先前已重命名)中的所有数据都进入新数据库,最后删除旧数据库 . 您最初可以使用 SQLite Manager FireFox plugin 创建数据库来执行创建sql语句 .
另一种选择是从sql文件内部创建数据库 . 这不是那么快,但如果数据库只有几个表,则延迟可能对用户来说是不明显的 . Upgrades are triggered by changing the database version number in the res/values/strings.xml file. 然后通过处理升级sql文件来完成升级 . 数据库中的数据将保持不变,除非删除其容器,例如删除表 .
The example below demonstrates how to use either method.
这是一个示例create_database.sql文件 . 它将被放置在项目的assets文件夹中以用于内部方法,或者复制到SQLite Manager的“Execute SQL”中以创建外部方法的数据库 . (NOTE: Notice the comment about the table required by Android.)
这里有一个示例update_database.sql文件 . 它将被放置在项目的assets文件夹中,用于内部方法,或者复制到SQLite Manager的“Execute SQL”中,以便为外部方法创建数据库 . (NOTE: Notice that all three types of SQL comments will be ignored by the sql parser that is included in this example.)
以下是添加到/res/values/strings.xml文件中的数据库版本号的条目 .
这是一个访问数据库然后使用它的活动 . ( Note: You might want to run the database code in a separate thread if it uses a lot of resources. )
这是数据库助手类,必要时可以在其中创建或更新数据库 . (NOTE: Android requires that you create a class that extends SQLiteOpenHelper in order to work with a Sqlite database.)
这是FileHelper类,它包含字节流复制文件和解析sql文件的方法 .
我的解决方案既不使用任何第三方库也不强制您在
SQLiteOpenHelper
子类上调用自定义方法以在创建时初始化数据库 . 它还负责数据库升级 . 所有需要做的就是继承SQLiteOpenHelper
.先决条件:
android_metadata
的1x1表,除了应用程序独有的表之外,其属性locale
的值为en_US
.对SQLiteOpenHelper进行子类化:
Subclass
SQLiteOpenHelper
.在
SQLiteOpenHelper
子类中创建private
方法 . 此方法包含将数据库内容从'assets'文件夹中的数据库文件复制到在应用程序包上下文中创建的数据库的逻辑 .覆盖
SQLiteOpenHelper
的onCreate
,onUpgrade
andonOpen
方法 .说够了 . 这里是
SQLiteOpenHelper
子类:最后,要获取数据库连接,只需在
SQLiteOpenHelper
子类上调用getReadableDatabase()
或getWritableDatabase()
,它将负责创建数据库,如果数据库不存在,则从'assets'文件夹中的指定文件复制数据库内容 .简而言之,您可以使用
SQLiteOpenHelper
子类访问assets文件夹中提供的数据库,就像在onCreate()
方法中使用SQL查询初始化的数据库一样 .SQLiteAssetHelper
库使这项任务非常简单 .它很容易添加为gradle依赖项(但是一个Jar也可用于Ant / Eclipse),并且可以在以下文档中找到它的文档:
https://github.com/jgilfelt/android-sqlite-asset-helper
Note: 此项目不再按上述Github链接所述进行维护 .
如文档中所述:
assets/databases
的子目录中 . 例如:assets/databases/my_database.db
(您可以选择在压缩文件中压缩数据库,例如
assets/databases/my_database.zip
. 这不是必需的,因为APK已经作为一个整体进行了压缩 . )从我所看到的,你应该运送一个已经有表设置和数据的数据库 . 但是,如果您需要(并且根据您拥有的应用程序类型),您可以允许“升级数据库选项” . 然后你要做的是下载最新的sqlite版本,获取在线托管的文本文件的最新插入/创建语句,执行语句并从旧数据库到新数据库进行数据传输 .
我修改了类和问题的答案,并编写了一个允许通过DB_VERSION更新数据库的类 .
使用课程 .
在activity类中,声明变量 .
在onCreate方法中,编写以下代码 .
如果将数据库文件添加到文件夹res / raw,则使用以下类的修改 .
http://blog.harrix.org/article/6784
我正在使用ORMLite,以下代码为我工作
请注意,代码从资产中的zip文件中提取数据库文件