如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:
预创建SQLite数据库,并将其包含在。apk? 在应用程序中包含SQL命令,并让它在第一次使用时创建数据库并插入数据?
我所看到的缺点是:
可能的SQLite版本不匹配可能会导致问题,我目前不知道数据库应该去哪里以及如何访问它。 在设备上创建和填充数据库可能需要很长时间。
有什么建议吗?关于任何问题的文档指针将非常感激。
如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:
预创建SQLite数据库,并将其包含在。apk? 在应用程序中包含SQL命令,并让它在第一次使用时创建数据库并插入数据?
我所看到的缺点是:
可能的SQLite版本不匹配可能会导致问题,我目前不知道数据库应该去哪里以及如何访问它。 在设备上创建和填充数据库可能需要很长时间。
有什么建议吗?关于任何问题的文档指针将非常感激。
当前回答
我写了一个库来简化这个过程。
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克隆。
其他回答
我终于做到了!!我已经使用这个链接帮助在Android应用程序中使用自己的SQLite数据库,但必须稍微改变一下。
If you have many packages you should put the master package name here: private static String DB_PATH = "data/data/masterPakageName/databases"; I changed the method which copies the database from local folder to emulator folder! It had some problem when that folder didn't exist. So first of all, it should check the path and if it's not there, it should create the folder. In the previous code, the copyDatabase method was never called when the database didn't exist and the checkDataBase method caused exception. so I changed the code a little bit. If your database does not have a file extension, don't use the file name with one.
它对我很有用,我希望它对你也有用
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.
}
将数据库导入apk,然后复制到/data/data/…将使数据库的大小增加一倍(1在apk, 1在data/data/…),并将增加apk的大小(当然)。所以你的数据库不应该太大。
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()
这篇博文中的代码。
我的解决方案既不使用任何第三方库,也不强迫您在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查询初始化数据库一样。
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); } }