从资产文件夹复制数据库后,我无法访问android pie上的数据库。它适用于所有其他版本。我在执行数据库操作时遇到了找不到此类列的错误。请帮我解决这个问题。下面是我正在使用的dbhelper类:
public class DBHelper extends SQLiteOpenHelper {
private static String DB_PATH = "/data/data/com.abc.xyz/databases/";
private static String DB_NAME = "db";
private SQLiteDatabase myDataBase;
private final Context myContext;
public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
/**
* Creates an empty database on the system and rewrites it with your own database.
* */
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exist
} else {
//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();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* 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);
checkDB.close();
} catch(SQLiteException e) {
//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 {
//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;
//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();
}
public void openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
@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) {
}
}
答案 0 :(得分:0)
我相信以下内容将解决在检查数据库是否存在时打开数据库的问题,而是检查数据库是否以文件形式存在(请参阅注释):-
public class DBHelper extends SQLiteOpenHelper {
//private static String DB_PATH = "/data/data/com.abc.xyz/databases/"; //<<<<<<<<<< overwritten
private static String DB_NAME = "db";
private SQLiteDatabase myDataBase;
private final Context myContext;
private int bytes_copied = 0;
private static int buffer_size = 1024;
private int blocks_copied = 0;
private boolean database_copied = false;
private boolean database_existed = false;
public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
// Check for and create (copy DB from assets) when constructing the DBHelper
if (!checkDataBase()) {
bytes_copied = 0;
blocks_copied = 0;
createDataBase();
} else {
database_existed = true;
}
}
/**
* Creates an empty database on the system and rewrites it with your own database.
* */
public void createDataBase() {
boolean dbExist = checkDataBase(); // Double check
if(dbExist){
//do nothing - database already exist
} else {
//By calling this method an 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();
try {
copyDataBase();
} catch (IOException e) {
File db = new File(myContext.getDatabasePath(DB_NAME).getPath());
if (db.exists()) {
db.delete();
}
e.printStackTrace();
throw new RuntimeException("Error copying database (see stack-trace above)");
}
}
}
/**
* 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() {
/**
* Does not open the database instead checks to see if the file exists
* also creates the databases directory if it does not exists
* (the real reason why the database is opened, which appears to result in issues)
*/
File db = new File(myContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
if (db.exists()) return true; // If it exists then return doing nothing
// Get the parent (directory in which the database file would be)
File dbdir = db.getParentFile();
// If the directory does not exits then make the directory (and higher level directories)
if (!dbdir.exists()) {
dbdir.mkdirs();
}
return 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 {
final String TAG = "COPYDATABASE";
//Open your local db as the input stream
Log.d(TAG,"Initiated Copy of the database file " + DB_NAME + " from the assets folder.");
InputStream myInput = myContext.getAssets().open(DB_NAME); // Open the Asset file
String dbpath = myContext.getDatabasePath(DB_NAME).getPath();
Log.d(TAG,"Asset file " + DB_NAME + " found so attmepting to copy to " + dbpath);
// Path to the just created empty db
//String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(dbpath);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[buffer_size];
int length;
while ((length = myInput.read(buffer))>0) {
blocks_copied++;
Log.d(TAG,"Ateempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes.");
myOutput.write(buffer, 0, length);
bytes_copied += length;
}
Log.d(TAG,
"Finished copying Database " + DB_NAME +
" from the assets folder, to " + dbpath +
String.valueOf(bytes_copied) + "were copied, in " +
String.valueOf(blocks_copied) + " blocks of size " +
String.valueOf(buffer_size) + "."
);
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
Log.d(TAG,"All Streams have been flushed and closed.");
}
public void openDataBase() throws SQLException {
//Open the database
//String myPath = DB_PATH + DB_NAME;
String myPath = myContext.getDatabasePath(DB_NAME).getPath();
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
@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) {
}
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
Log.d("DBCONFIGURE","Database has been configured ");
}
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
Log.d("DBOPENED","Database has been opened.");
}
}
以下活动用于测试上述内容(注意,实例化DBHelper对象 myDBHlpr 会启动数据库检查/复制):-
public class MainActivity extends AppCompatActivity {
DBHelper myDBhlpr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("VERSION","Build Version is " + String.valueOf(Build.VERSION.SDK_INT));
myDBhlpr = new DBHelper(this);
Cursor csr = myDBhlpr.getWritableDatabase().query(
"sqlite_master",
null,null,null,null,null,null
);
while (csr.moveToNext()) {
Log.d(
"DBITEMS",
"Found " + csr.getString(csr.getColumnIndex("name")) +
" which has a type of " + csr.getString(csr.getColumnIndex("type")) +
" the SQL used to create it, was :-\n\t" + csr.getString(csr.getColumnIndex("sql"))
);
}
csr.close();
}
}
- 请注意日志
/VERSION: Build Version is 28
中的行,即Android Pie
发生异常,其中包括指向InputStream myInput = myContext.getAssets().open(DB_NAME);
的错误的堆栈跟踪(即资产不存在,如预期的那样。):-
2019-01-25 17:51:12.303 30371-30371/so.cdfa D/VERSION: Build Version is 28
2019-01-25 17:51:12.304 30371-30371/so.cdfa D/COPYDATABASE: Initiated Copy of the database file db from the assets folder.
2019-01-25 17:51:12.304 30371-30371/so.cdfa W/System.err: java.io.FileNotFoundException: db
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.content.res.AssetManager.nativeOpenAsset(Native Method)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.content.res.AssetManager.open(AssetManager.java:744)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.content.res.AssetManager.open(AssetManager.java:721)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at so.cdfa.DBHelper.copyDataBase(DBHelper.java:101)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at so.cdfa.DBHelper.createDataBase(DBHelper.java:55)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at so.cdfa.DBHelper.<init>(DBHelper.java:36)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at so.cdfa.MainActivity.onCreate(MainActivity.java:18)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.app.Activity.performCreate(Activity.java:7136)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.app.Activity.performCreate(Activity.java:7127)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
2019-01-25 17:51:12.305 30371-30371/so.cdfa W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
2019-01-25 17:51:12.306 30371-30371/so.cdfa W/System.err: at android.os.Looper.loop(Looper.java:193)
2019-01-25 17:51:12.307 30371-30371/so.cdfa W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6669)
2019-01-25 17:51:12.307 30371-30371/so.cdfa W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2019-01-25 17:51:12.307 30371-30371/so.cdfa W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
2019-01-25 17:51:12.307 30371-30371/so.cdfa W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2019-01-25 17:51:12.307 30371-30371/so.cdfa D/AndroidRuntime: Shutting down VM
2019-01-25 17:51:12.312 30371-30371/so.cdfa E/AndroidRuntime: FATAL EXCEPTION: main
Process: so.cdfa, PID: 30371
java.lang.RuntimeException: Unable to start activity ComponentInfo{so.cdfa/so.cdfa.MainActivity}: java.lang.RuntimeException: Error copying database (see stack-trace above)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.RuntimeException: Error copying database (see stack-trace above)
at so.cdfa.DBHelper.createDataBase(DBHelper.java:62)
at so.cdfa.DBHelper.<init>(DBHelper.java:36)
at so.cdfa.MainActivity.onCreate(MainActivity.java:18)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2019-01-25 17:51:12.330 30371-30371/so.cdfa I/Process: Sending signal. PID: 30371 SIG: 9
复制到Assets文件夹并重命名为 db 的人造sqlite数据库文件。复制数据库并将其项目(表索引等)写入日志,例如:-
再次记下构建版本和日志记录
2019-01-25 17:54:51.734 30587-30587/so.cdfa D/VERSION: Build Version is 28
2019-01-25 17:54:51.735 30587-30587/so.cdfa D/COPYDATABASE: Initiated Copy of the database file db from the assets folder.
2019-01-25 17:54:51.735 30587-30587/so.cdfa D/COPYDATABASE: Asset file db found so attmepting to copy to /data/user/0/so.cdfa/databases/db
2019-01-25 17:54:51.736 30587-30587/so.cdfa D/COPYDATABASE: Ateempting copy of block 1 which has 1024 bytes.
............. lines removed from brevity
2019-01-25 17:54:51.746 30587-30587/so.cdfa D/COPYDATABASE: Ateempting copy of block 40 which has 1024 bytes.
2019-01-25 17:54:51.746 30587-30587/so.cdfa D/COPYDATABASE: Finished copying Database db from the assets folder, to /data/user/0/so.cdfa/databases/db40960were copied, in 40 blocks of size 1024.
2019-01-25 17:54:51.746 30587-30587/so.cdfa D/COPYDATABASE: All Streams have been flushed and closed.
2019-01-25 17:54:51.770 30587-30587/so.cdfa D/DBCONFIGURE: Database has been configured
2019-01-25 17:54:51.772 30587-30587/so.cdfa D/DBOPENED: Database has been opened.
2019-01-25 17:54:51.772 30587-30587/so.cdfa D/DBITEMS: Found sqlite_sequence which has a type of table the SQL used to create it, was :-
CREATE TABLE sqlite_sequence(name,seq)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found Schedules which has a type of table the SQL used to create it, was :-
CREATE TABLE `Schedules` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`date` REAL
)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found type_affinity_example which has a type of table the SQL used to create it, was :-
CREATE TABLE type_affinity_example (
id INTEGER PRIMARY KEY, -- <<<<<<<< MUST BE INTEGER VALUE ELSE INVALID DATATYPE
col1 rumplestiltskin,
col2 BLOB,
col3 INTEGER,
col4 NUMERIC,
col5 REAL,
col6 REALINT, -- because of rule 1 will be INTEGER type affinity NOT REAL!!!!
col7 TEXT
)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found OHLCV_Components which has a type of table the SQL used to create it, was :-
CREATE TABLE OHLCV_Components (symbol TEXT, tradeDate TEXT, openPrice REAL, highPrice REAL, lowPrice REAL, closePrice REAL, volume INTEGER)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found league which has a type of table the SQL used to create it, was :-
CREATE TABLE league (league_id INTEGER PRIMARY KEY, league_name TEXT)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found team which has a type of table the SQL used to create it, was :-
CREATE TABLE team (team_id INTEGER PRIMARY KEY, team_name TEXT)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found player which has a type of table the SQL used to create it, was :-
CREATE TABLE player (player_id INTEGER PRIMARY KEY, player_name)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found team_league_history which has a type of table the SQL used to create it, was :-
CREATE TABLE team_league_history (
tlh_team_reference INTEGER,
tlh_league_reference INTEGER,
tlh_from_date TEXT DEFAULT CURRENT_DATE,
tlh_to_date TEXT DEFAULT '3000-12-31'
)
2019-01-25 17:54:51.773 30587-30587/so.cdfa D/DBITEMS: Found player_team_history which has a type of table the SQL used to create it, was :-
CREATE TABLE player_team_history (
pth_player_reference INTEGER,
pth_team_reference INTEGER,
pth_from_date TEXT DEFAULT CURRENT_DATE,
pth_to_date TEXT DEFAULT '3000-12-31'
)
2019-01-25 17:54:51.774 30587-30587/so.cdfa D/DBITEMS: Found android_metadata which has a type of table the SQL used to create it, was :-
CREATE TABLE android_metadata (locale TEXT)
注意,其格式如上,可以复制非sqlite数据库文件(任何文件),这将导致数据库异常损坏。检查文件的前16个字节是否符合SQlite文件头( 头字符串:“ SQLite格式3 \ 000” )Database File Format可以否定这个问题。
注意,因为已经创建了数据库本身(尽管为空),您需要将其删除。您可以通过删除应用程序的数据或卸载应用程序来执行此操作(您也可以可以通过设备资源管理器将其删除。)
2019-01-25 17:59:32.868 30666-30666/so.cdfa D/VERSION: Build Version is 28
2019-01-25 17:59:32.873 30666-30666/so.cdfa D/DBCONFIGURE: Database has been configured
2019-01-25 17:59:32.874 30666-30666/so.cdfa D/DBOPENED: Database has been opened.
2019-01-25 17:59:32.875 30666-30666/so.cdfa D/DBITEMS: Found sqlite_sequence which has a type of table the SQL used to create it, was :-
CREATE TABLE sqlite_sequence(name,seq)
2019-01-25 17:59:32.875 30666-30666/so.cdfa D/DBITEMS: Found Schedules which has a type of table the SQL used to create it, was :-
CREATE TABLE `Schedules` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`date` REAL
...........
答案 1 :(得分:0)
我要做的就是在 createDataBase()中的 this.getReadableDatabase()之后关闭数据库连接:
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exist
} else {
//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();
this.close()
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
答案 2 :(得分:0)
我一直为此苦苦挣扎。只需从createDatabase()方法中删除以下代码行即可解决该问题:
this.getReadableDataBase();
似乎现在可以使用API 27和28正常工作了。