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.
Related
Say in version 1, I do not have this SharedPreference value.
Now, I manually updated the APK to version 2. After that I re-boot my phone. However, when I adb pull the .xml file that was created, it is set as false.
SharedPreference as below:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean optimus = prefs.getBoolean("prime", true);
I thought getBoolean sets the value as the default value it it does not exist (which doesn't. It's the first time it's being initialized).
This xml file is completely new and it did not exist before.
Why does this happen? Is there some native Android behavior I'm missing?
If you update the application (without first uninstalling), it will keep the preference that has been set in the previous version. So if "prime" was set to false previously in the old version then it will still be false in the new version, unless the old version is uninstalled first, before installing the new one. Uninstalling would delete the old preferences xml file.
Also, rebooting your phone will not clear preferences.
getBoolean(key, defaultValue) returns the defaultValue programmatically when this Java statement is executed if there is no value in the XML file for this key, so you won't find the defaultValue in the XML file you're talking about unless you call prefs.edit().putBoolean(key, myValue).apply()
Edit: In other words, prefs.getBoolean("prime", true) will always return true UNLESS you once called prefs.edit().putBoolean("prime", false).apply() at any point in time (from the time you installed the first version of the application and assuming you never uninstalled it or cleared data).
I'm using this library to show the main features of the app, currently I'm using shared preferences variable like this,
1- After installation I set a SharedPreferences variable x to 0
2- First time the user opens the main activity I check the variable x, if it 0 I use ShowCase
view and set x to 1
3- Now every time the user opens the app I will check x, if it's 1 I skip the ShowCase view
I'm new in Android and I'm not if it's good idea to check SharedPreferences on the main thread every time the app is opened, any one thinks I should be doing something else instead? or is this good enough?
It's absolutely ok. Of course it depends on time of retrieving of x variable from SharedPreferences. I suppose if you save few million variables in SharedPreferences then time of retrieving will significantly grow and user will notice it.
When you think about performance just test your case because general recommendations could not work in particular case.
I wonder if it is possible to create a SQLite database when the app got run the first time and already save some data in it.
I don't want to save it every time the app gets started. Just after Installation.
Is this possible?
Just override onCreate() method in class which extends from SQLiteOpenHelper and insert there some data.
Called when the database is created for the first time. This is where
the creation of tables and the initial population of the tables should
happen.
http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#onCreate(android.database.sqlite.SQLiteDatabase) .
For my apps I do the simple trick as in Splash check preference of available Data variable, first time its default is false. Then I read from Database and check is there any items If No then insert data and set preference available data variable to true.
and when you come next time splash it will check again and in your available data variable will get true so you don't need to do anything and continue your flow.
I hope this will help you too :)
SQLiteOpenHelper.onUpgrade should do the trick, simply compare oldVersion with newVersion
When I originally wrote and published my app, I was using a custom written activity to handle application settings. I used custom file name to store shared preferfences, like this:
getSharedPreferences("custom_settings_file",MODE_PRIVATE);
But now I'm refactoring my app and I would like to implement PreferenceActivity or PreferenceFragment and an xml file with PreferenceScreen section. Every tutorial or example that I've seen is using
getDefaultSharedPreferences(context);
to retrieve shared preferences, because PreferenceActivity assumes default filename to store preferences and there's no way to tell it to use a different one(at least I couldn't find one after an hour of searching and reading documentation).
So now I have a problem. If I just simply use the new default file, existing users of my app will lose their settings when they update the app, because the new application will not know anything about "custom_settings_file". What would be the best way to move the data from an old file to a new one on app update?
Here are the possible options that I could come up with:
Extend Application class and implement a piece of code in onCreate() so that every time my app is launched, it would check for existence of "custom_settings_file" and move it's contents to the new one. But running a block of code on every app launch seems like wasting too much processing resources for an operation that only needs to run once.
Just notify the user that their old settings are gone. But obviously this is not acceptable.
Is there a better solution, than option 1? Perhaps someone has already faced a similar problem?
What is preventing you from doing number 1 only once?
Just add a "migration" boolean to the new sharedpreferences.
If you also load the xml preference file then you can try this:
PreferenceManager.setDefaultValues(context, YOUR_PREFERENCE_NAME, MODE_PRIVATE, R.xml.preference_file, false);
If not (you want to add each preference item dynamically in your code) then you can do like this:
PreferenceManager pm = getPreferenceManager();
pm.setSharedPreferencesMode(MODE_PRIVATE);
pm.setSharedPreferencesName(YOUR_PREFERENCE_NAME);
In case you still want to use the defaultSharedPreference and process the migration then ... I'm writing this and I see Nicklas's answer, so I'm done here.
Could you add value in your new SharedPreferences that records whether you are a new install or an upgrade. If you don't have the setting in your sharedpreferences, check to see if you have an old preferences file in the way you were before. Then convert those preferences to your new method, and set your private setting indicating that it's been upgraded. Then just set the new value indicating the new state and you won't need to check your old preferences any more.
I need to recognize first launch of my application or activity.
At this time I need to get some information from server create local database and save info to it. What is the best way to do this?
Create any preferences for example FirstLaunch and set true \ false to it.
Check whether my database exists or not.
Something else?
PS. All server calls must be into one transaction. Ormlite supports transactions?
Thanks.
For the "create database at first run"-purpose, you should use an SQLiteOpenHelper, which offers you the onCreate()-method that is called when:
[...] the database is created for the first time.
The Database-file itself will be created for you (you don't have to do this manually). In this method, you can then perform actions like populating your database with standard entry's.
If you want to populate the database with informations you get from your server, there might be a problem when there is no Internet-connection available.
In this case, I would check if there is a connection available:
If there is, get your informations.
If not, show a Toast or some other notification to inform the user.
To determine if your Database has be populated with the standard entry's, you can use the database-version which is also provided by the SQLiteDatabase-class:
When you first create your Database-object, you call
SQLiteOpenHelpers constructor and pass it 0 as the Database
version.
If you successfully populated your database, you use
setVersion()-method to alter it to 1.
Later in the onOpen()-method, which is called when the
database is opened, you can check if the database was populated by
using the getVersion()-method.
If it is populated, call the super-method to open it.
If not, try populating it.
Further more, the getReadableDatabase() / getWritableDatabase()-methods should be called off the main-thread anyways because:
Database upgrade may take a long time, you should not call this method
from the application main thread, including from
ContentProvider.onCreate().
So getting the informations from the Internet can take place in the onCreate() and in the onOpen()-method (if it wasn't successful at the first try). You can (for example) use a Service to do this.
If you want to solve this problem with database:
Create database with MyDatabasaVersion table and store your version in a single row, for example db_version default value is 0. First time when the application starts you check the db_version if 0 you need to start the syncronisation, after it is finishing set the db_version to 1.
The easiest way should be sharedpreferences. you can call it everywhere form the application context and you can put boolean values in it.
Here are all Android storages.
you should try first option Create any preferences for example FirstLaunch and set true \ false to it.