Your issue is very likely with mDBHelper.getReadableDatabase();
That is with Android 9+ the default logging has been changed from Journal to WAL (write=ahead logging).
What getReadableDatabase (or getWritableDatabase) does is open a brand new empty database and populates it with a table android_metadata. The actual data is not written to the database file but to the -wal file (i.e. database file name suffixed with -wal).
When the database is copied, the database created by the getReadableDatabase is overwritten but the -wal (and also -shm) files remain. So when the database is opened SQLite detects that the -wal file is not the correct -wal file and fails to open it. The SDK handles this by creating a new empty usable database and hence the table name not found.
There are a number of fixes.
You can override the onConfigure method of the database helper and call disableWriteAheadLogging
- This is not recommended as you lose the advantages of WAL
You could use the method getReadableDatabase for a database with another name.
- I've never come across this, but in theory it would work. Again this is not recommended as it's still relatively resource hungry.
You can delete the -wal and -shm files if they exist before or immediately after the copy.
- This is not recommended as it's wasteful of resources (opening a database is relatively expense resource wise).
You can replace the getReadableDatabase with a File's mkdirs. That is the getReadableDatabase has been coded, historicaly, as a hack to get around the NO ENT error due to the databases folder not existing when an app is installed.
I recommend not actually doing this after the check for the database existing but as part of the check using something based upon :-
private boolean checkDataBase(Context context, String databaseName) {
/**
* 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(context.getDatabasePath(databaseName).getPath()); //Get the file name of the database
Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove for Live App
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()) {
db.getParentFile().mkdirs();
dbdir.mkdirs();
}
return false;
}
The code above was taken from A pre-populated database does not work at API 28 throws “no such table” exception
, which is a little more comprehensive.
Fix
Applied to your code then it could be :-
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvProduct = (ListView) findViewById(R.id.listview_product);
mDBHelper = new DatabaseHelper(this);
File database = getApplicationContext().getDatabasePath(DatabaseHelper.DBNAME);
if (false == database.exists()) {
File dbdir = database.getParentFile();
if (!dbdir.exists()) {
database.getParentFile().mkdirs();
dbdir.mkdirs();
}
//Copy db
if (copyDatabase(this)) {
Toast.makeText(this, "Copy database succes", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Copy data error", Toast.LENGTH_SHORT).show();
return;
}
}
}