我必须为我的天文学课程设计一个项目,所以我继续进行基本的琐事游戏。我将所有问题,答案和解释存储在多个SQLite数据库中(每个星球一个)。当我构建APK并将其安装在我的Android模拟器上时,所有问题,答案等。不在那里。我认为这是因为数据库未包含在APK文件本身中。我的问题是,在安装APK时如何包括数据库?
我尝试在Google上进行搜索,并能够找到一些据说可以将它们放入资产文件夹的内容,但是我不知道它们的含义。如果这是我要做的全部,那么资产文件夹在哪里?
答案 0 :(得分:0)
您要做的是将使用SQlite管理工具(例如,用于SQLite,Navicat等的数据库浏览器)通常开发的预先存在的数据库复制到资产文件夹(必须是资产/数据库文件夹)如果使用SQLiteAssetHelper (see readme))。
需要将数据库从资产文件夹(资产/数据库文件夹)复制到合适的位置(SQLiteAssetHelper是一种方式),但仅当数据库不存在时才需要复制。尽管经常建议使用SQLiteAssetHelper,但不幸的是,它似乎不适用于Android 9+(Pie)。因此,您不妨看看Entire DBHelper the above code was extracted from, in this answer
此答案是使用SQLiteAssetHelper [Reading a database from the assets folder资产文件夹中的数据库的分步指南)。如果不使用SQLiteAssetHelper,则可以执行步骤1-3(创建/复制到数据库文件夹除外)
您必须相应地更改DBNAME,并且这希望数据库文件位于资产文件夹中,而不是资产/数据库中。如果文件包含DBNAME,则该文件名将带有扩展名。
资产将成为APK的一部分(尺寸限制允许)。
如果使用SQLiteAssetHelper,则每个数据库需要一个。每个链接的数据库助手可以多次使用,适用于多个数据库。
以下示例复制了3个预先存在的数据库,即mydb001,mydb002和mydb003。前两个位于资产文件夹中,第三个已放置在资产/数据库文件夹中(用于演示)。
如果需要复制数据库,将复制它们。如果以前已复制它们,则不会重新复制。
使用了两个类OpenAssetDBHelper.java和MainActivity.java。前者是完成所有工作的工具。请注意,它具有相当广泛的日志记录,因此非常冗长。值得注意的是,它最多可以处理10个多重数据库助手,但是可以通过更改public static final int MAXIMUM_HELPERS = 10;
来更改。
已在模拟的Android Pie设备上对此进行了检查。
:-
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扩展了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.