Room inflates database with old data - android

I am trying to auto-populate a database on first app load. I am trying to 'inflate' the database from a local copy like so:
public static synchronized bDB getInstance(Context context) {
if(bDB == null) {
Log.v("Hello", "Inflating database.");
bDB = Room.databaseBuilder(context.getApplicationContext(), BDB.class, databaseName)
.createFromAsset("database/bdb.db")
.fallbackToDestructiveMigration()
.build();
}
return bDB;
}
I noticed an error in the initial file (data error, schema remains the same). I corrected it and updated the file 'bdb.db'. However, every time the database gets pre-populated, it always picks up the old, erroneous data. I have tried the following:
Cleared and invalidated all caches.
Cleaned and rebuilt.
Checked the database file a million times.
While the file in the assets folder is correct, where is it picking up the old data from ?
Any help is most welcome.

Based on the discussion in the comments, you seem to have been bitten by auto-backup. With Room, this more typically manifests with a "cannot verify data integrity" error due to schema changes — in your case, the schema was the same, but the data was different.
You can configure auto-backup, both in terms of disabling it (as you did, setting android:allowBackup to false) or controlling what files get backed up (using android:fullBackupContent). And, you can do this on a per-build variant basis, such as having different values for a debug or a release build:
For android:allowBackup, you should be able to point to a boolean resource, with different values in main and debug
For android:fullBackupContent, you can have different XML resources in main and debug with different rules

Related

Accessing assets of main application inside package

i'm currently trying to develop a package for a Flutter App, with Kotlin. My issue is that I need to provide the package with a config file, which should only be defined inside the main App. Since the config differs for the Dev and Prod environment, the app should pass through the path of the File via the Method Channel. The problem is that the package isn't able to access the assets folder of the calling application.
Path: "assets/config.json" (the root being the main application)
Steps I already tried:
Creating the file inside the res/raw & accessing the config file through a ressource id -> Kotlin gives me an "Unresolved reference" error, unless I create the file inside the packages res/raw
Instead of passing through the path, I tried passing through the content of the config & writing it into an empty temporary file. The code in Kotlin like this:
val config = File(applicationContext.filesDir,"config.json")
config.writeText(configContent)
-> This works, but it seems like a weird solution to the problem.
please let me know if I need to provide further information & thank you in advance!
edit:
The Java Method that is called during initialisation:
public static void createMultipleAccountPublicClientApplication(#NonNull final Context context,
#NonNull final File configFile,
#NonNull final IMultipleAccountApplicationCreatedListener listener)
Flutter assets aren't files - they are packaged up and only available through the rootBundle. So, if you want to make a file from a text asset, someone has to load the asset and write it to a file.
As your plugin user will be in charge of the asset, they will have to do the first part (and will end up with a String). The question arises of who should do the writing.
You could make the plugin user use path_provider to find the temporary directory and write it there and then pass you the file path. Eventually, down in the Java, you new File(theTempFilePath). Or they could pass the string to the Dart half of your plugin and you create the temp file in the same way.
It's probably more convenient if they pass your plugin the string, you pass that to the native side and have the native side create a temporary file and write the string there. (BTW, I assume we are talking about this config file: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-configuration#how-to-use-a-configuration-file )
See this answer for creating temporary files: Creating temporary files in Android
Note that there's actually no reason that your plugin user then needs to use an asset. They could, instead, just hard code the string in their code if the configuration never really changes.
There's an argument that as this is a JSON configuration file, you may not want to bother your user with the details of this JSON configuration file. You may want to default it in your Dart code (why not hard code it as a string, as above, if it never really changes) and then provide some methods to override particular values like the client id and the redirect uri, which may be the only things that users ever change in practice. So rather than making them supply a complete JSON file, they just give you those two strings and you plonk them into your default JSON. Maybe a version 2 feature :)

greenDAO and data validation

I'm looking into using greenDAO for my Android app, but I noticed it doesn't seem to support any kind of data validation other than "not null", "unique", and foreign keys, either on the SQL level (constraints defined when creating tables) or the Java level (validation logic in setter methods). "Keep sections" don't seem like they would be helpful in this case because you can't have them within individual methods. Am I missing something, or would I really need to add yet another layer on top of the generated Java objects if I wanted to validate input data? (I'm somewhat confused how the framework could be useful without providing any place to include validation logic.)
1.
You can write a method
boolean check ();
in KEEP-SECTION of the entity which you call manually before INSERT or UPDATE.
2.
Another possibility is to extend the sourcecode of greendao generator to support checks: In property.java you could add a method to Property.Builder
public Property.Builder check (String expr) {
property.checkConditon = expr;
}
Of course you would have to introduce the String checkCondition = ""; and use it for generating the dao in the dao-template.
Problem:
With new versions of greendao your changes would be lost (but then again new version may already contain such a feature)
3.
A third possibility is to copy the generated CREATE TABLE statement, modify it to fit your needs and call your modified statement instead of the original one or to drop the original table and call your statement.
Problem:
If your table changes you will have to repeat this.

Android Pushing Updates on Play Store

I have an app that depends on SQLite for data which is populated by xmls shipped with the app in the assets folder.
When you run the app the first time it sets a shared preference config_run = false.
then i check if config_run = false then parse the xml and dump the data into db
set config_run = true
Now i realize that when i have to push an update on Google Play and add more content into the XML. Even though i change the database version from 1 to 2. The import script wont run because shared preference config_run value will be set to true.
Any pointers on how to handle this situation ?
Scenarios
First Instal - Ver = 1, DB V = 1 (Data is parsed and dumped into the database)
Bugs Fixed and push and update but no data has changed - ver = 1.1, DB V = 1 (It should just replace the code and not upgrade or re-create the database)
Upgraded the DATA and pushed a new update - ver 1.2, DB = 2 ( No new code but data has to be re-created)
The Flow of My App
The App Starts Splash Activity. If Shared Pref - config_run is equal to false then it starts a Progress Dialog and parses and dumps the data into the database.
Upon Parsing and Creating DB and dumping data it goes to MainActivity.
Second Case
SplashActivity Runs and config_run = true so directly goes to MAin Activity.
As Suggested by few people below if i try to dumb the data into the database in onUpgrade of the SQLiteHelper it will happen only in MAinActivity as i dont open a Db connection in the SplashActivity and the Dialog Progress wont be displayed also.
Add a shared pref of the version number you last ran the script for.
On app start, check the current apk version, and if newer, run the script again and update the pref
Why dont you want use built in sqlite versioning system. DB version is independed from app version. And it does exactly what you want. SQLiteOpenHelper? Every time tou change your db version an onUpgrate callback will be called and you can refill your db. There are a lot of examples.
Instead of setting your shared pref (config_run) to false and making it true, just set the database version into it. When you update your app, check whether you have the same version number in your shared pref. You can do this as shown below:
configRun = settings.getInt("database_version", 0);
if ((DBAdapter.DATABASE_VERSION) == configRun)
{
//skip xml parsing
}
else
{
//first time configRun will be "0" and DBAdapter.DATABASE_VERSION will be 1
// so you need to parse your xml here and set configRun =1
//on update, change your DB version to 2. now again your configRun and DBAdapter.DATABASE_VERSION will mismatch and you can parse your xml.
}
Have your xmlfiles end with the date of the update, and store the last updated date in sharedpref.
On launch you can check search for updates ( in an optimized way ) and if you find a new file with a new date when compared to the last time you know you need to dump the file.
Total hack job :D
Two things you can do:
The right way: Override database provider's onUpdate() to import the file. (as suggested above)
The one line changer: Instead of check for key="config_run", you check and set for key=("config_run"+DB_VERSION) to see if import is needed, and of course, if the key does not exist, you should return false.
This way every time you update the DB number, import job will run again.
This is agnostic to your app version.

Is closing a database opened with window.openDatabase necessary?

The code at the moment reads something in the order of...
DoAnything() {
OpenTheDatabase()
// ... Do all the things! ...
}
However, the database object is never closed. This is worrisome.
The database is opened as follows:
var db = window.openDatabase( ... paramters ... );
No .closeDatabase function exists, or the documentation is incomplete. I thought the following might suffice:
db=null;
I see that sqlite3_close(sqlite3*) and int sqlite3_close_v2(sqlite3*) exist, but I'm unsure how to apply them in this case.
How do I close the database, and is it necessary?
Generally you only have one database connection that you open on app startup, and there is no need to close it while the app is open. It's a single threaded, single user app, so a lot of the normal rules about database connections don't apply.
When the app shuts down, you can rely on the browser to close everything - given the average quality of code on the web, browsers have to be pretty good at cleanup.
Setting db to null and letting the garbage collector do its thing will probably also work, but it is better not to create the extra objects in the first place.

Android concerns with restore database

Imagine the following scenario (I allow backup / restore from my app, I'm doing backup / restore white the entire file.db):
The user make backup of the database.
In the future I do an update on my application which has a new version of the database.
what would happen if the user restore the database with the old version?
How can I avoid this kind of problem?
It would be a good idea to use BackupHelper? I did a simulation of the worst scenario of my database and gave 20k, BackupHelper is recommended for less than 1mb, it would be a good idea to use it? I think my app will never exceed 100kb.
You access SQLite databases via a SQLiteOpenHelper, which provides three callbacks: onCreate(), onUpgrade() and onOpen().
The constructor declares a version parameter, and internally it compares the database version (which is stored in the file) with the supplied argument. If the requested version is newer than the file's, your onUpgrade(db, old, new) is called and you get a chance to alter tables, fill rows and so on.
The restore procedure should simply close all open cursors and helpers and copy the new file in.
May be this is not the best approach but you can do it as:
1- Store the DB Version in the database.
2- After restoring the database, check the DB Version and do the required changes accordingly. Like
void afterRestoration()
{
long dbVersion = get from db;
if(dbVersion == 1)
{
alter table1 add column1;
}
else
{
}
}

Categories

Resources