Как включить несколько баз данных при установке APK-файла? - PullRequest
0 голосов
/ 29 апреля 2019

Я должен был сделать проект для своего класса астрономии, поэтому я пошел дальше и сделал простую игру викторины. Я сохранил все вопросы, ответы и объяснения в нескольких базах данных SQLite (по одному для каждой планеты). Когда я собираю APK и устанавливаю его на свой эмулятор Android, все вопросы, ответы и т. Д. не там Я полагаю, это потому, что базы данных не включены в сам файл APK. У меня вопрос, как включить базы данных при установке APK?

Я попытался найти его в Google и смог найти кое-что, что, по его словам, помещало их в папку ресурсов, но я понятия не имел, что они имели в виду. Если это все, что мне нужно сделать, где находится папка ресурса?

1 Ответ

0 голосов
/ 29 апреля 2019

Что вы делаете, это копируете ранее существующие базы данных, разработанные в основном с помощью инструмента управления SQlite (например, DB Browser для SQLite, Navicat и т. Д.), В папку активов (должна быть папка assets / database, если используя SQLiteAssetHelper (см. readme) ).

  • Обратите внимание, что вам может понадобиться создать папку (и)
  • Обратите внимание, что лучше всего копировать файл вне Android Studio, так как Android Studio может попытаться открыть файл, что может вызвать путаницу.
  • Обратите внимание, что база данных сохранена (я рекомендую закрыть инструмент и снова открыть его для проверки).

Базы данных должны быть скопированы из папки ресурсов (папка assets / database) в подходящее место (SQLiteAssetHelper - один из способов), но только когда база данных не существует. Хотя SQLiteAssetHelper часто рекомендуется, к сожалению, он не работает на Android 9+ (пирог). Таким образом, вы можете захотеть взглянуть на Весь DBHelper, из которого был извлечен приведенный выше код, в этом ответе

  • Этот ответ представляет собой пошаговое руководство по использованию SQLiteAssetHelper [ Чтение базы данных из папки активов база данных из папки ресурсов). Можно выполнить шаги 1-3 (кроме создания / копирования в папку базы данных), если не используется SQLiteAssetHelper

  • Необходимо соответственно изменить имя DBNAME, и это предполагает, что файл базы данных будет находиться в папке ресурсов, а не в ресурсах / базах данных. DBNAME будет именем файла с расширением, если оно есть у файла.

  • Код относительно длинный, поскольку включает ведение журнала для целей отладки. Скорее всего, вы захотите удалить отладку до развертывания приложения.

Активы будут частью APK (если позволяют ограничения по размеру).

Если вы используете SQLiteAssetHelper, вам нужно по одному на базу данных. Помощник по базам данных по ссылке может быть использован несколько раз, адаптированы для нескольких баз данных.

* * Пример 1 040 * +1041 *

В следующем примере копируются 3 уже существующие базы данных, а именно mydb001, mydb002 и mydb003. Первые 2 находятся в папке активов, третий был помещен в папку активов / баз данных (для демонстрации).

Если базы данных необходимо скопировать, они будут. Если они были скопированы ранее, они не копируются повторно.

Существует два класса, используемых OpenAssetDBHelper.java и MainActivity.java. Бывший класс, который делает всю работу. Обратите внимание, что он имеет довольно обширное ведение журнала и, следовательно, довольно длинный. Следует отметить, что он может обрабатывать несколько помощников базы данных до 10, но это можно изменить, изменив public static final int MAXIMUM_HELPERS = 10;.

Это было проверено на эмулируемом устройстве Android Pie.

OpenAssetDBHelper.java

: -

public class OpenAssetDBHelper extends SQLiteOpenHelper {

    private static final String LOGTAG = "OADB-HLPR";
    public static final int MAXIMUM_HELPERS = 10;
    private String mDBPath, mAssetPath;
    private static OpenAssetDBHelper mInstance[] = new OpenAssetDBHelper[MAXIMUM_HELPERS];
    private SQLiteDatabase mDB;

    /**
     * OpenAssetDBHelper Class that will copy a predefined database
     * from the assets folder and then open it is the database;
     *
     * The copy will only be done if the database does not exist.
     *
     * Note this code is intended to be used for development and/or
     * experimentation, hence the extensive logging.
     */

    /**
     * get an OpenAssetDBHelper instance as a singleton;
     * Note! caters for up to 10 OpenAssetDBHelpers for up to 10 databases
     * as specified by the helper_index
     *
     * @param helper_index  Index to this instance/database
     *                      (0-MAXIMUM_HELPERS less 1)
     * @param context       Context for the database
     * @param database      Database name (i.e. file name)
     * @param dbversion     Database version (user_version)
     * @param assetfile     Name of the asset file to be copied to the database
     * @param asset_sub_directories String Array of the sub-directories where
     *                              the asset file is located;
     *                              MUST be in order
     * @return              The resultant OpenAssetDBHelper
     */
    public static synchronized OpenAssetDBHelper getInstance(
            int helper_index,
            Context context,
            String database,
            int dbversion,
            String assetfile,
            String[] asset_sub_directories) {
        // Checck that helper_index is within bounds
        if (helper_index > (MAXIMUM_HELPERS -1)) {
            throw new RuntimeException(
                    "Helper Index greater than " +
                            MAXIMUM_HELPERS
            );
        }
        if (helper_index < 0) {
            throw new RuntimeException(
                    "Helper Index cannot be negative, must be 0-" +
                            MAXIMUM_HELPERS
            );
        }
        // Create the respective OpenAssetDBHelper instance
        if(mInstance[helper_index] == null) {
            mInstance[helper_index] = new OpenAssetDBHelper(context,
                    database,
                    dbversion,
                    assetfile,
                    asset_sub_directories);
        }
        return mInstance[helper_index];
    }

    /**
     * Construct an OpenAssetDBHelper instance;
     * Note! can only be called within class
     *
     * @param context       the context to be used
     * @param database      the database name (equates to filename)
     * @param dbversion     the databaae version (user_version)
     * @param assetfile     The name of the asset file i.e. the pre-defined db
     * @param directories   The hierarchy of directories within the assets folder
     *                      where the asset file is located
     *                      (null or zero elements = in the assets folder)
     */
    private OpenAssetDBHelper(Context context,
                              String database,
                              int dbversion,
                              String assetfile,
                              String[] directories) {
        super(context, database, null, dbversion);
        Log.d(LOGTAG,"OpenAssetDBHelper being constructed.");

        mDBPath = context.getDatabasePath(database).getPath();
        if (assetfile == null || assetfile.length() < 1) {
            assetfile = database;
        }
        mAssetPath = buildAssetPath(directories,assetfile);

        if (!ifDatabaseExists(mDBPath)) {
            Log.d(LOGTAG,"Database " + database + " not found at " + mDBPath + " so attempting to copy from assets.");
            if (copyDatabaseFromAssets(context,mDBPath, database, mAssetPath)) {
                Log.d(LOGTAG, "Successfully Copied Database from Assets.");
            } else {
                throw new RuntimeException("No Database Available.");
            }
        }
        // Force Database open and store it
        this.mDB = this.getWritableDatabase();
        //logDatabaseTableInformation(mDB);
        Log.d(LOGTAG,"OpenAssetDBHelper constructed.");
    }


    /**
     * onCreate - This is where you would create tables;
     * Typically this is where you would alter the structure of the database;
     * Note that this is called once for the lifetime of the database.
     * @param db The SQLitedatabase
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " initiated.");
        // As Database is copied from assets nothing to do in onCreate!
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " completed.");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " initiated.");
        // Nothing to do as it's early days in the Database's lifetime.
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " completed.");
    }

    /**
     * Check to see if the Database exists,
     * if it doesn't exists then check to see if
     * the database directory exists,
     * if the directory(ies) does(do) not exist then make the directory(ies);
     *
     *
     * @param dbpath        The path to the database
     * @return              true if the database exists, else false
     */
    private boolean ifDatabaseExists(String dbpath) {
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " initiated.");
        File db = new File(dbpath);
        if(db.exists()) return true;
        File dir = new File(db.getParent());
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return false;
    }

    /**
     * Copy the Database from the assets folder
     * @param context
     * @param dbpath
     * @return
     */
    private boolean copyDatabaseFromAssets(Context context,String dbpath, String dbname, String asset) {
        String thisclass = new Object(){}.getClass().getEnclosingMethod().getName();
        Log.d(LOGTAG,thisclass + " initiated");
        InputStream assetsdb;
        OutputStream database;
        File db = new File(dbpath);
        int filesize;
        // Get the asset file
        try {
            Log.d(LOGTAG,thisclass + " attempting to find asset " + asset);
            assetsdb = context.getAssets().open(asset);
            filesize = assetsdb.available();
            Log.d(LOGTAG,thisclass + " asset " + asset +
                    " located successfully with a size of " +
                    Integer.toString(filesize)
            );
        } catch (IOException e) {
            Log.d(LOGTAG,thisclass + " Did not locate asset " + asset);
            e.printStackTrace();
            return false;
        }

        // Read the first 16 bytes from the asset file
        byte[] dbcheck = new byte[16];
        try {
            assetsdb.read(dbcheck,0,16);
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass + " failed trying to read 16 bytes to check for a valid database. ");
            e.printStackTrace();
            return false;
        }

        // Check that the asset file is an SQLite database
        String chkdb = new String(dbcheck);
        if(!chkdb.equals("SQLite format 3\u0000")) {
            Log.d(LOGTAG,thisclass + " asset " +
                    asset +
                    " is not a valid SQLite Database File (found " +
                    chkdb +
                    " at bytes 1-16 instead of SQLite format 3)");
            try {
                assetsdb.close();
            } catch (IOException e) {
                // Not worth doing anything
            }
            return false;
        }
        // Close the asset file
        try {
            assetsdb.close();
        } catch (IOException e) {
            Log.d(LOGTAG,thisclass +
                    " failed to close assets file after checking for a valid database."
            );
            return false;
        }
        // Re-open the asset file
        try {
            assetsdb = context.getAssets().open(asset);
            filesize = assetsdb.available();
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass +
                    " failed trying to re-open asset " +
                    asset +
                    " after checking for a valid database."
            );
            e.printStackTrace();
            return false;
        }

        // Read the entire asset file into a buffer
        Log.d(LOGTAG, thisclass +
                " copying asset database " +
                dbname +
                " into buffer of size " +
                filesize
        );
        byte[] buffer = new byte[filesize];
        // Close the asset file
        try {
            assetsdb.read(buffer);
            Log.d(LOGTAG,thisclass +
                    " closing asset database " + dbname
            );
            assetsdb.close();
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass +
                    " failed while copying asset database " +
                    dbname +
                    " (or closing asset database)."
            );
            e.printStackTrace();
            return false;
        }
        // Open the new database file
        try {
            Log.d(LOGTAG,thisclass + " attempting to open new database file " + dbpath);
            database = new FileOutputStream(dbpath);
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass + " failed to open new database file.");
            e.printStackTrace();
            return false;
        }
        // Write the new database file
        try {
            Log.d(LOGTAG, thisclass + " writing new database file " + dbpath);
            database.write(buffer);
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass + " failed while writing new database file " + dbpath);
            e.printStackTrace();
            return false;
        }
        // Flush the new database file
        try {
            Log.d(LOGTAG, thisclass + " flushing new database file " + dbpath);
            database.flush();
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass + " failed while flushing new database file " + dbpath);
            e.printStackTrace();
            return false;
        }
        // Close the new database file
        try {
            Log.d(LOGTAG, thisclass + " closing new database file " + dbpath);
            database.close();
        } catch (IOException e) {
            Log.d(LOGTAG, thisclass + " failed while closing new database file " + dbpath);
            e.printStackTrace();
            return  false;
        }
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " completed.");
        return true;
    }

    /**
     * Log Database table Information
     */
    private void logDatabaseTableInformation(SQLiteDatabase db) {
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " initiated.");
        String mastertable = "sqlite_master";
        String typecolumn = "type";
        String namecolumn = "name";
        String sqlcolumn = "sql";
        String[] args = new String[]{"table","android_metadata"};
        Cursor csr = db.query(mastertable,
                null,
                typecolumn + "=? AND " + namecolumn + "!=?",
                args,
                null,null,null
        );
        while (csr.moveToNext()) {
            Log.d(LOGTAG,"Database contains Table " +
                    csr.getString(csr.getColumnIndex(namecolumn)) +
                    " created by SQL " +
                    csr.getString(csr.getColumnIndex(sqlcolumn))
            );
            logTableInformation(db, csr.getString(csr.getColumnIndex(namecolumn)));
        }
        csr.close();
        Log.d(LOGTAG,new Object(){}.getClass().getEnclosingMethod().getName() + " completed.");
    }

    /**
     * Write Table information, Table name, Column Count,
     * Row Count and Column Names to the Log
     * @param table Name of the table to be reported on
     */
    private void logTableInformation(SQLiteDatabase db, String table) {
        Cursor csr = db.query(table,
                null,
                null,
                null,
                null,
                null,
                null
        );
        Log.d(LOGTAG,"Table is " +
                table +
                " Column Count = " +
                Integer.toString(csr.getColumnCount()) +
                " Row Count = " +
                Long.toString(DatabaseUtils.queryNumEntries(mDB,table))
        );
        StringBuilder columns_as_string = new StringBuilder();
        for (String s: csr.getColumnNames()) {
            columns_as_string.append(s).append(" ");
        }
        Log.d(LOGTAG, "\tColumns are :- " + columns_as_string);
        csr.close();
    }

    /**
     * Build the sub-path to the asset, according to the directories specified
     *
     * @param directories   directories underneath the assets folder where
     *                      the asset files is located, null or empty
     *                      array if file is located directly in the
     *                      assets folder;
     *                      directories must be specified in the order
     *                      in which they appear in the path.
     * @param filename      The filename of the asset
     * @return              The fill sub-path to the asset
     */
    private String buildAssetPath(String[] directories, String filename) {
        StringBuilder sb = new StringBuilder();
        final String SEPERATOR = "/";
        if (directories != null && directories.length > 0) {
            for (String s: directories) {
                sb.append(s);
                if (!s.substring(s.length()-1,s.length()).equals(SEPERATOR)) {
                    sb.append(SEPERATOR);
                }
            }
            sb.append(filename);
            return sb.toString();
        } else {
            return filename;
        }
    }

    public static OpenAssetDBHelper getHelperInstance(int instance) {
        if (instance < mInstance.length && instance >= 0) {
            return mInstance[instance];
        }
        else return mInstance[0];
    }
}
  • Код может быть значительно сокращен, он длинен, поскольку предназначен для помощи в разработке и понимании.

MainActivity.java

: -

открытый класс MainActivity расширяет AppCompatActivity {

public static final int DBVERSION = 1;

public static final String DBNAME1 = "mydb001"; // in assets
public static final int DB1_INDEX = 0;
public static final String DBNAME2 = "mydb002"; // in assets
public static final int DB2_INDEX = 1;
public static final String DBNAME3 = "mydb003"; //in assets/databases
public static final int DB3_INDEX = 2;

OpenAssetDBHelper[] mDBHlpr = new OpenAssetDBHelper[3];

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Example 1 - Database file located in the assets folder and
    //              database name same as file name
    mDBHlpr[0] = OpenAssetDBHelper.getInstance(DB1_INDEX,
            this,
            DBNAME1,
            DBVERSION,
            DBNAME1,
            null //<<<<<<<<<< null = db file (pre-existing db) located in the assets folder
    );
    // mydb002
    mDBHlpr[1] = OpenAssetDBHelper.getInstance(DB2_INDEX,
            this,
            DBNAME2,
            DBVERSION,
            "mydb002",
            null
    );


    // mydb003 in the assets/database folder
    mDBHlpr[2] = OpenAssetDBHelper.getInstance(DB3_INDEX,
            this,
            DBNAME3,
            DBVERSION,
            "mydb003",
            new String[]{"databases"} //<<<<<<<<<<< array of sub-folders under assets (order matters)
    );

    // Do something with each Database
    for (OpenAssetDBHelper o:mDBHlpr) {
        logDBSchemaInfo(o.getWritableDatabase());
    }
}

//Dump/Log SQLitemaster
private void logDBSchemaInfo(SQLiteDatabase db) {
    DatabaseUtils.dumpCursor(
            db.query(
                    "sqlite_master",null,null,null,null,null,null
            )
    );
}

}

Результат

Журнал для первого запуска (в данном случае) включает: -

2019-04-29 15:41:56.329 D/OADB-HLPR: ifDatabaseExists initiated.
2019-04-29 15:41:56.330 D/OADB-HLPR: Database mydb001 not found at /data/user/0/mjt.openmultipleassetdataabses/databases/mydb001 so attempting to copy from assets.
2019-04-29 15:41:56.331 D/OADB-HLPR: copyDatabaseFromAssets initiated
2019-04-29 15:41:56.331 D/OADB-HLPR: copyDatabaseFromAssets attempting to find asset mydb001
2019-04-29 15:41:56.331 D/OADB-HLPR: copyDatabaseFromAssets asset mydb001 located successfully with a size of 77824
2019-04-29 15:41:56.331 D/OADB-HLPR: copyDatabaseFromAssets copying asset database mydb001 into buffer of size 77824
2019-04-29 15:41:56.332 D/OADB-HLPR: copyDatabaseFromAssets closing asset database mydb001
2019-04-29 15:41:56.332 D/OADB-HLPR: copyDatabaseFromAssets attempting to open new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb001
2019-04-29 15:41:56.332 D/OADB-HLPR: copyDatabaseFromAssets writing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb001
2019-04-29 15:41:56.332 D/OADB-HLPR: copyDatabaseFromAssets flushing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb001
2019-04-29 15:41:56.332 D/OADB-HLPR: copyDatabaseFromAssets closing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb001
2019-04-29 15:41:56.333 D/OADB-HLPR: copyDatabaseFromAssets completed.
2019-04-29 15:41:56.333 D/OADB-HLPR: Successfully Copied Database from Assets.
2019-04-29 15:41:56.353 D/OADB-HLPR: OpenAssetDBHelper constructed.



2019-04-29 15:41:56.353 D/OADB-HLPR: OpenAssetDBHelper being constructed.
2019-04-29 15:41:56.354 D/OADB-HLPR: ifDatabaseExists initiated.
2019-04-29 15:41:56.354 D/OADB-HLPR: Database mydb002 not found at /data/user/0/mjt.openmultipleassetdataabses/databases/mydb002 so attempting to copy from assets.
2019-04-29 15:41:56.354 D/OADB-HLPR: copyDatabaseFromAssets initiated
2019-04-29 15:41:56.354 D/OADB-HLPR: copyDatabaseFromAssets attempting to find asset mydb002
2019-04-29 15:41:56.354 D/OADB-HLPR: copyDatabaseFromAssets asset mydb002 located successfully with a size of 77824
2019-04-29 15:41:56.354 D/OADB-HLPR: copyDatabaseFromAssets copying asset database mydb002 into buffer of size 77824
2019-04-29 15:41:56.355 D/OADB-HLPR: copyDatabaseFromAssets closing asset database mydb002
2019-04-29 15:41:56.355 D/OADB-HLPR: copyDatabaseFromAssets attempting to open new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb002
2019-04-29 15:41:56.355 D/OADB-HLPR: copyDatabaseFromAssets writing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb002
2019-04-29 15:41:56.355 D/OADB-HLPR: copyDatabaseFromAssets flushing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb002
2019-04-29 15:41:56.355 D/OADB-HLPR: copyDatabaseFromAssets closing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb002
2019-04-29 15:41:56.355 D/OADB-HLPR: copyDatabaseFromAssets completed.
2019-04-29 15:41:56.356 D/OADB-HLPR: Successfully Copied Database from Assets.
2019-04-29 15:41:56.375 D/OADB-HLPR: OpenAssetDBHelper constructed.



2019-04-29 15:41:56.375 D/OADB-HLPR: OpenAssetDBHelper being constructed.
2019-04-29 15:41:56.375 D/OADB-HLPR: ifDatabaseExists initiated.
2019-04-29 15:41:56.375 D/OADB-HLPR: Database mydb003 not found at /data/user/0/mjt.openmultipleassetdataabses/databases/mydb003 so attempting to copy from assets.
2019-04-29 15:41:56.375 D/OADB-HLPR: copyDatabaseFromAssets initiated
2019-04-29 15:41:56.375 D/OADB-HLPR: copyDatabaseFromAssets attempting to find asset databases/mydb003
2019-04-29 15:41:56.376 D/OADB-HLPR: copyDatabaseFromAssets asset databases/mydb003 located successfully with a size of 77824
2019-04-29 15:41:56.376 D/OADB-HLPR: copyDatabaseFromAssets copying asset database mydb003 into buffer of size 77824
2019-04-29 15:41:56.376 D/OADB-HLPR: copyDatabaseFromAssets closing asset database mydb003
2019-04-29 15:41:56.376 D/OADB-HLPR: copyDatabaseFromAssets attempting to open new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb003
2019-04-29 15:41:56.377 D/OADB-HLPR: copyDatabaseFromAssets writing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb003
2019-04-29 15:41:56.377 D/OADB-HLPR: copyDatabaseFromAssets flushing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb003
2019-04-29 15:41:56.377 D/OADB-HLPR: copyDatabaseFromAssets closing new database file /data/user/0/mjt.openmultipleassetdataabses/databases/mydb003
2019-04-29 15:41:56.377 D/OADB-HLPR: copyDatabaseFromAssets completed.
2019-04-29 15:41:56.377 D/OADB-HLPR: Successfully Copied Database from Assets.
2019-04-29 15:41:56.394 D/OADB-HLPR: OpenAssetDBHelper constructed.

Последующий прогон

2019-04-29 16:05:29.905 D/OADB-HLPR: OpenAssetDBHelper being constructed.
2019-04-29 16:05:29.905 D/OADB-HLPR: ifDatabaseExists initiated.
2019-04-29 16:05:29.910 D/OADB-HLPR: OpenAssetDBHelper constructed.
2019-04-29 16:05:29.910 D/OADB-HLPR: OpenAssetDBHelper being constructed.
2019-04-29 16:05:29.910 D/OADB-HLPR: ifDatabaseExists initiated.
2019-04-29 16:05:29.914 D/OADB-HLPR: OpenAssetDBHelper constructed.
2019-04-29 16:05:29.914 D/OADB-HLPR: OpenAssetDBHelper being constructed.
2019-04-29 16:05:29.915 D/OADB-HLPR: ifDatabaseExists initiated.
2019-04-29 16:05:29.918 D/OADB-HLPR: OpenAssetDBHelper constructed.
...