如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:
预创建SQLite数据库,并将其包含在。apk? 在应用程序中包含SQL命令,并让它在第一次使用时创建数据库并插入数据?
我所看到的缺点是:
可能的SQLite版本不匹配可能会导致问题,我目前不知道数据库应该去哪里以及如何访问它。 在设备上创建和填充数据库可能需要很长时间。
有什么建议吗?关于任何问题的文档指针将非常感激。
如果您的应用程序需要一个数据库,并且它带有内置数据,那么交付该应用程序的最佳方式是什么?我应该:
预创建SQLite数据库,并将其包含在。apk? 在应用程序中包含SQL命令,并让它在第一次使用时创建数据库并插入数据?
我所看到的缺点是:
可能的SQLite版本不匹配可能会导致问题,我目前不知道数据库应该去哪里以及如何访问它。 在设备上创建和填充数据库可能需要很长时间。
有什么建议吗?关于任何问题的文档指针将非常感激。
当前回答
我使用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文件中提取数据库文件
其他回答
我终于做到了!!我已经使用这个链接帮助在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.
}
我的解决方案既不使用任何第三方库,也不强迫您在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查询初始化数据库一样。
我修改了类和问题的答案,并编写了一个允许通过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
如果所需的数据不是很大(限制我不知道,将取决于很多东西),你也可以从网站/网络应用下载数据(XML, JSON,无论什么)。接收到数据后,使用接收到的数据执行SQL语句,创建表并插入数据。
如果你的移动应用程序包含大量数据,稍后用更准确的数据或更改更新已安装的应用程序中的数据可能会更容易。
我写了一个库来简化这个过程。
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克隆。