首页 文章

使用数据库发送应用程序

提问于
浏览
904

如果您的应用程序需要数据库并且内置数据,那么运送该应用程序的最佳方式是什么?我是不是该:

  • 预创建SQLite数据库并将其包含在 .apk 中?

  • 在应用程序中包含SQL命令并让它创建数据库并在首次使用时插入数据?

我看到的缺点是:

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

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

有什么建议?关于任何问题的文档的指针将不胜感激 .

16 回答

  • 8

    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”加上后上下文引导挂钩,以便在上下文中运行启动后操作) . 这使您可以在应用程序附带紧密耦合的数据;在大多数情况下,这适合我的用例 .

  • 444

    我刚刚在一篇名为Using your own SQLite database in Android applications的文章中找到了在 ReignDesign blog 中做到这一点的方法 . 基本上你预先创建你的数据库,把它放在你的apk资产目录中,并首先使用复制到 /data/data/YOUR_PACKAGE/databases/ 目录 .

  • 2

    我写了一个library来简化这个过程 .

    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克隆它 .

  • 126

    2017年11月谷歌发布了Room Persistence Library

    从文档:

    Room持久性库为SQLite提供了一个抽象层,以便在利用SQLite的全部功能的同时进行流畅的数据库访问 . 该库可帮助您在运行应用程序的设备上创建应用程序数据的缓存 . 此缓存作为应用程序的唯一事实来源,允许用户在应用程序中查看关键信息的一致副本,无论用户是否具有Internet连接 .

    首次创建或打开数据库时,Room数据库具有回调功能 . 您可以使用create callback来填充数据库 .

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

    代码来自blog post .

  • 2

    在Android Studio 3.0中将应用程序与数据库文件一起发送

    使用数据库文件运送应用程序对我来说是一个好主意 . 优点是您不需要进行复杂的初始化,如果您的数据集很大,有时会花费大量时间 .

    Step 1: Prepare database file

    准备好您的数据库文件 . 它可以是.db文件或.sqlite文件 . 如果使用.sqlite文件,则只需更改文件扩展名即可 . 步骤是一样的 .

    在这个例子中,我准备了一个名为testDB.db的文件 . 它有一个表和一些样本数据就像这样
    enter image description here

    Step 2: Import the file into your project

    如果您还没有,请创建资产文件夹 . 然后将数据库文件复制并粘贴到此文件夹中

    enter image description here

    Step 3: Copy the file to the app's data folder

    您需要将数据库文件复制到应用程序的数据文件夹,以便与其进行进一步的交互 . 这是复制数据库文件的一次性操作(初始化) . 如果多次调用此代码,数据文件夹中的数据库文件将被assets文件夹中的数据库文件覆盖 . 当您希望在将来更新应用程序期间更新数据库时,此覆盖过程非常有用 .

    请注意,在应用更新期间,不会在应用的数据文件夹中更改此数据库文件 . 只有卸载才能将其删除 .

    需要将数据库文件复制到 /databases 文件夹 . 打开Device File Explorer . 输入 data/data/<YourAppName>/ 位置 . 这是上面提到的应用程序的默认数据文件夹 . 默认情况下,数据库文件将放在此目录下名为databases的另一个文件夹中

    enter image description here

    现在,复制文件进程与Java正在做的非常相似 . 使用以下代码执行复制粘贴 . 这是启动代码 . 它还可以用于将来更新(通过覆盖)数据库文件 .

    //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
    }
    

    然后刷新文件夹以验证复制过程

    enter image description here

    Step 4: Create database open helper

    SQLiteOpenHelper 创建一个子类,包括connect,close,path等 . 我把它命名为 DatabaseOpenHelper

    import android.content.Context;
    import android.database.SQLException;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    public class DatabaseOpenHelper extends SQLiteOpenHelper {
        public static final String DB_NAME = "testDB.db";
        public static final String DB_SUB_PATH = "/databases/" + DB_NAME;
        private static String APP_DATA_PATH = "";
        private SQLiteDatabase dataBase;
        private final Context context;
    
        public DatabaseOpenHelper(Context context){
            super(context, DB_NAME, null, 1);
            APP_DATA_PATH = context.getApplicationInfo().dataDir;
            this.context = context;
        }
    
        public boolean openDataBase() throws SQLException{
            String mPath = APP_DATA_PATH + DB_SUB_PATH;
            //Note that this method assumes that the db file is already copied in place
            dataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.OPEN_READWRITE);
            return dataBase != null;
        }
    
        @Override
        public synchronized void close(){
            if(dataBase != null) {dataBase.close();}
            super.close();
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
    

    Step 5: Create top level class to interact with the database

    这将是读写数据库文件的类 . 还有一个示例查询可以打印出数据库中的值 .

    import android.content.Context;
    import android.database.Cursor;
    import android.database.SQLException;
    import android.database.sqlite.SQLiteDatabase;
    import android.util.Log;
    
    public class Database {
        private final Context context;
        private SQLiteDatabase database;
        private DatabaseOpenHelper dbHelper;
    
        public Database(Context context){
            this.context = context;
            dbHelper = new DatabaseOpenHelper(context);
        }
    
        public Database open() throws SQLException
        {
            dbHelper.openDataBase();
            dbHelper.close();
            database = dbHelper.getReadableDatabase();
            return this;
        }
    
        public void close()
        {
            dbHelper.close();
        }
    
        public void test(){
            try{
                String query ="SELECT value FROM test1";
                Cursor cursor = database.rawQuery(query, null);
                if (cursor.moveToFirst()){
                    do{
                        String value = cursor.getString(0);
                        Log.d("db", value);
                    }while (cursor.moveToNext());
                }
                cursor.close();
            } catch (SQLException e) {
                //handle
            }
        }
    }
    

    Step 6: Test running

    通过运行以下代码行来测试代码 .

    Database db = new Database(context);
    db.open();
    db.test();
    db.close();
    

    点击运行按钮并加油!

    enter image description here

  • 194

    我想直到今天最好和最新的方式是使用 SQLiteAssetHelper 类 .

    This tutorial通过在Android中导入和使用外部数据库完美指导您

    Android SQLiteAssetHelper库允许您在桌面计算机中构建SQLite数据库,并在Android应用程序中导入和使用它 . 让我们创建一个简单的应用程序来演示这个库的应用 . 步骤1:使用您喜欢的SQLite数据库应用程序创建数据库quotes.db(DB Browser for SQLite是一个可移植的跨平台免费软件,可用于创建和编辑SQLite数据库) . 使用单个列'quote'创建一个表'quotes' . 在表'引号'中插入一些随机引号 . 第2步:数据库可以直接导入到项目中,也可以作为压缩文件导入项目 . 如果您的数据库太大,建议使用压缩文件 . 您可以创建ZIP压缩或GZ压缩 . 如果使用ZIP压缩,则压缩db文件的文件名必须为quotes.db.zip;如果使用GZ压缩,则必须为quotes.db.gz . 步骤3:使用软件包名称com.javahelps.com.javahelps.externaldatabasedemo创建一个新的应用程序外部数据库演示 . 步骤4:打开build.gradle(Module:app)文件并添加以下依赖项 . 依赖{
    编译'com.readystatesoftware.sqliteasset:sqliteassethelper:'
    }
    保存build.gradle文件后,单击“立即同步”链接以更新项目 . 您可以通过右键单击build.gradle文件并选择Synchronize build.gradle选项来同步build.gradle . 第5步:右键单击app文件夹并创建新的assets文件夹 . 步骤6:在assets文件夹中创建一个新文件夹'databases' . 步骤7:将quotes.db.zip文件复制并粘贴到assets / databases文件夹中 . 步骤8:创建一个新类DatabaseOpenHelper包com.javahelps.externaldatabasedemo;

    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实例;

    / **
    *私有构造函数,以避免从外部类创建对象 .
    *

    • @param上下文
    • /
      private DatabaseAccess(Context context){
      this.openHelper = new DatabaseOpenHelper(context);
      }

    / **
    *返回DatabaseAccess的单例实例 .
    *

    • @param上下文
    • @return DabaseAccess的实例
    • /
      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();
      }
      }

    / **
    *阅读数据库中的所有报价 .
    *

    • @return一系列报价
    • /
      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

  • 5

    最后我做到了!!我使用过这个链接help Using your own SQLite database in Android applications,但不得不稍微改变一下 .

    • 如果您有许多包,则应将主包名称放在此处:

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

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

    • 在前面的代码中,当数据库不存在且 checkDataBase 方法导致异常时,从未调用 copyDatabase 方法 . 所以我稍微更改了代码 .

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

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

    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.
    
    }
    
  • 2

    目前没有办法预先创建一个与您的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秒钟,我不知道这对真正的手机需要多长时间,但只有当用户首次启动应用程序时才会发生一次 .

  • 3

    在apk中运送数据库然后将其复制到 /data/data/... 将使数据库的大小加倍(apk中为1, data/data/... 中为1),并且将增加apk大小(当然) . 所以你的数据库不应该太大 .

  • 24

    如果所需的数据不是太大(限制我不知道,取决于很多事情),您也可以从网站/ webapp下载数据(XML,JSON等) . 接收后,使用接收的数据执行SQL语句,创建表并插入数据 .

    如果您的移动应用程序包含大量数据,则稍后可能更容易使用更准确的数据或更改来更新已安装应用程序中的数据 .

  • 3

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

    以下是添加到/res/values/strings.xml文件中的数据库版本号的条目 .

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

    这是一个访问数据库然后使用它的活动 . ( 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(";");
        }
    
    }
    
  • 4

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

    先决条件:

    • 您希望随应用程序一起提供的数据库 . It should contain 一个名为 android_metadata 的1x1表,除了应用程序独有的表之外,其属性 locale 的值为 en_US .

    对SQLiteOpenHelper进行子类化:

    • Subclass SQLiteOpenHelper .

    • SQLiteOpenHelper 子类中创建 private 方法 . 此方法包含将数据库内容从'assets'文件夹中的数据库文件复制到在应用程序包上下文中创建的数据库的逻辑 .

    • 覆盖 SQLiteOpenHelperonCreateonUpgrade and 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() ,它将负责创建数据库,如果数据库不存在,则从'assets'文件夹中的指定文件复制数据库内容 .

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

  • 5

    SQLiteAssetHelper 库使这项任务非常简单 .

    它很容易添加为gradle依赖项(但是一个Jar也可用于Ant / Eclipse),并且可以在以下文档中找到它的文档:
    https://github.com/jgilfelt/android-sqlite-asset-helper

    Note: 此项目不再按上述Github链接所述进行维护 .

    如文档中所述:

    • 将依赖项添加到模块的gradle构建文件中:
    dependencies {
        compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
    }
    
    • 将数据库复制到assets目录中,名为 assets/databases 的子目录中 . 例如:
      assets/databases/my_database.db

    (您可以选择在压缩文件中压缩数据库,例如 assets/databases/my_database.zip . 这不是必需的,因为APK已经作为一个整体进行了压缩 . )

    • 创建一个类,例如:
    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);
        }
    }
    
  • 4

    从我所看到的,你应该运送一个已经有表设置和数据的数据库 . 但是,如果您需要(并且根据您拥有的应用程序类型),您可以允许“升级数据库选项” . 然后你要做的是下载最新的sqlite版本,获取在线托管的文本文件的最新插入/创建语句,执行语句并从旧数据库到新数据库进行数据传输 .

  • 10

    我修改了类和问题的答案,并编写了一个允许通过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;
        }
    }
    

    使用课程 .

    在activity类中,声明变量 .

    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

  • 12

    我正在使用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文件中提取数据库文件

相关问题