android - 如何ship一个安卓应用程序和数据库?

  显示原文与译文双语对照的内容

如果你的应用程序需要一个数据库,并附带了数据,那么最好的方法是什么?

1 ) 重新创建SQLite数据库并将它的包含在apk中?

2 ) 包含应用程序的SQL命令,并让它创建数据库并在第一次使用时插入数据?

我看到的缺点

1 ) 可能的SQLite版本不匹配可能导致问题,我目前不知道数据库应该去哪里以及如何访问它。

2 ) 在设备上创建和填充数据库可能需要很长时间。

你的想法指向文档的关于任何问题的指针都会很棒。

时间:

使用自己的SQLite数据库在Android软件,我刚找到了一种方法来做着受ReignDesign博客中一篇题为 基本上,你需要预创建数据库,将它放在你的apk中的assets 目录中,然后在第一次使用复制到"/data/data/YOUR_PACKAGE/databases/" 目录。

创建和更新数据库有两个选项。

一个是在外部创建数据库,然后将它放在项目的assets 文件夹中,然后从那里复制整个数据库。 如果数据库有很多表和其他组件,那么这将快得多。 升级是由改变 res/values/strings.xml 文件中的数据库版本号。 升级将是通过外部创建一个新的数据库完成,取代了老的数据库在 assets 文件夹使用新的数据库中,保存在内部存储在另一个名字下,复制新的旧的数据库数据库数据库( 已经被重命名为) 从 assets 文件夹转换为内部存储,传输的所有数据从旧到新的数据库中,最后删除旧的数据库。 你可以创建一个数据库最初通过使用 Firefox 插件 SQLite管理器来执行你创建的sql语句。

另一个选项是从一个sql文件内部创建一个数据库。 这不是快但延迟可能会忽略用户如果数据库只有几个表。 升级是由改变 res/values/strings.xml 文件中的数据库版本号。 然后,通过处理升级sql文件来完成 。升级。 数据库中的数据将保持不变,除非它的容器被删除,例如删除一个表。

下面的例子展示了如何使用两种方法。

下面是一个 create_database.sql 文件示例。 是放在 assets 文件夹项目的内部方法或复制到"执行 SQL'sqlite经理创建数据库的外部方法。 ( 注意:请注意关于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 文件夹项目的内部方法或复制到"执行 SQL'sqlite经理创建数据库的外部方法。 ( 注意:注意所有三种类型的SQL注释都将被这个示例中包含的SQL解析器忽略。)


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

下面是一个添加到/res/values/strings.xml 文件中用于数据库版本号的条目。


<item type="string" name="databaseVersion" format="integer">1</item>

下面是一个访问数据库并使用它的Activity 。 ( 注意:你可能想要运行一个单独的线程中的数据库代码如果它使用很多资源。 )


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

 }
}

下面是数据库 helper 类,在必要时创建或者更新数据库。 ( 注意:Android需要创建一个扩展SQLiteOpenHelper的类,以便与SQLite数据库一起工作。)


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.
 */

}

下面是包含字节流复制文件和解析sql文件的FileHelper类的类。


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

}

SQLiteAssetHelper库使得这个任务非常简单。
jar文件和文档在 https://github.com/jgilfelt/android-sqlite-asset-helper 。xml中找到。

如文档中所述:

  1. android-sqlite-asset-helper.jar 添加到项目 libs 目录中。
  2. 压缩zip文件中的数据库,如 assets/databases/database_name.zip
  3. 创建一个类:

例如:


public class MyDatabase extends SQLiteAssetHelper {
 private static final String DATABASE_NAME ="database_name";
 private static final int DATABASE_VERSION = 1; 
 public MyDatabase(Context context) {
 super(context, DATABASE_NAME, null, DATABASE_VERSION); 
 } 
 }

  1. 完成:- )

将数据库中的数据库传送到 /data/data/...,然后将它的复制到将使数据库( 1中的1,data/data/... 中的)的大小加倍,并增加数据库大小( 当然) 。 所以你的数据库不应该太大。

从我所看到的,你应该正在传送一个已经有表设置和数据的数据库。 如果你想要( 根据你的应用程序类型),你可以允许"升级数据库选项"。 然后,你所做的就是下载最新的SQLite版本,获取联机托管的文本文件的最新插入/创建语句,执行语句并从旧数据库执行数据传输到新数据库。

最后我做到了我已经使用这个链接帮助使用自己的android应用程序sqlite数据库,但必须改变这一点。!

  1. 如果你有许多软件包,你应该在这里放置主软件包名:

    private static String DB_PATH ="data/data/masterPakageName/databases";

  2. 我更改了将数据库从本地文件夹复制到 模拟器 文件夹的方法 ! 文件夹不存在时出现了一些问题。 首先,它应该检查路径,如果不存在,它应该创建文件夹。

  3. 在前面的代码中,当数据库不存在时,copyDatabase 方法从不被调用,并且 checkDataBase 方法导致异常。 所以我稍微修改了一下代码。

  4. 如果你的数据库没有扩展名,请不要使用文件名。

它对我来说很好,我希望它对你也有用


 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.

}

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

下面的线程有最好的讨论/解决方案,我发现了很好的示例代码。

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

我将创建语句存储为一个字符串资源,以便用 Context.getString() 读取,并用 SQLiteDatabse.execSQL() 运行它。

我在 res/raw/inserts.sql ( 我创建了sql文件,7000 + 行) 中存储插入的数据。 使用这项技术从上面的链接我进入了一个循环,逐行读取文件和concactenated数据到"插入到tbl值",另一个 SQLiteDatabase.execSQL() 。 保存 7000"插入到tbl值"时没有意义,因为它们可以是 concactenated 。

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

Android已经提供了一个数据库管理的version-aware方法。 这个方法已经在BARACUS框架的框架中被利用了。

使你能够管理整个版本的应用程序的数据库,它可以从以前的版本更新SQLite数据库到当前版本。

还允许你运行SQLite的hot-backups和 hot-recovery 。

我不是 100%确定,但一个hot-recovery特定装置可以使你船准备数据库应用程序。 但是我不确定数据库二进制格式对于特定的设备,供应商或者设备生成的格式是否明确。

由于这些内容是Apache许可 2,所以可以自由地重用代码的任何部分,可以在github上找到

编辑:

如果你只想发送数据,你可以考虑在应用程序首次启动时实例化并持久化 pojo 。 BARACUS获得了对这个( 用于配置信息的内置键值存储,比如"app_first_run"加上after-context-bootstrap钩子以便在上下文上运行post-launch操作)的内置支持。 这使得你可以在你的应用中提供紧密耦合的数据;在大多数情况下,这些都适合我的用例。

接收后,使用接收的数据执行SQL语句创建表并插入数据。

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

Jaco

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

先决条件:

  1. 你想随应用程序一起发货的数据库。 它应该包含 1x1名为 android_metadatalocale 拥有一个属性值的表 en_US 除了表应用程序所特有的。

子类化 SQLiteOpenHelper:

  1. 子类 SQLiteOpenHelper
  2. 创建一个 privateSQLiteOpenHelper 子类中的方法。 这里方法包含将数据库内容从'assets'文件夹中的数据库文件复制到在应用程序包上下文中创建的数据库的逻辑。
  3. 覆盖 onCreateonUpgradeonOpenSQLiteOpenHelper的方法。

已经足够了。这里是 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:
 }
 }
}

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

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

...