I'm writing an app that comes packaged with an SQLite database.
I'm in the process of gradually adding to the database, but when I compile the code in Android Studio the app doesn't see the latest update to it.
The workaround I've found is changing the filename of the database and updating it in the code, but that's going to get very tiresome if I'm making frequent updates, and I feel there must be a better way.
For what it's worth, here's the relevant code snippet:
public class DatabaseHelper extends SQLiteAssetHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "database5.db";
private static final String BOOKS = "books";
private static final String AUTHORS = "authors";
public DatabaseHelper (Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// etc
}
You need to increment the version number then use the setForcedUpgrade(). Something like this:
private static final int DATABASE_VERSION = 2;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// call this method to force a database overwrite every time the version number increments:
//setForcedUpgrade();
}
You can found the details in the sample project.
when I compile the code in Android Studio the app doesn't see the latest update to it
SQLiteAssetHelper only copies the database out of assets if the database does not already exist.
I feel there must be a better way
If you are not modifying the database at runtime, and if you plan on distributing updates to that database in the form of fresh assets, you can use setForcedUpgrade(), as is covered in the documentation.
Otherwise, you can uninstall the app when you change the database, or clear the app's data in Settings when you change the database.
Just change your sqlite database file name from asset folder
[here i have change name Quran.db to Quran1.db]
And from class where you have access it.
[from class also i have changed Quran.db to Quran1.db]
This method worked fine for me.
Related
I'm building an Android quiz application which needs to use database locally. SQLite seems to be what I need.
The database will contain questions and answers (which will be inputted by me manually not by using the application), and some tables which store users's score, progress etc.
My question is how do I create tables and import data from other source (or insert with INSERT INTO queries) into Android's embedded SQLite manually? Online tutorials only teach me how to do it from the scratch and via application interface...
There are various ways you could do this.
You could create the database populated with the questions and answers as an asset in the assets folder. This pre-existing database is then copied once, perhaps utilising the SQLiteAssetHelper to manage the copy of the database.
There are numerous SQLite management type tools available, such as SQlite studio, DB Browser for SQlite, Navicat, that could be used to create the database and load the data. You then just copy the saved file into the assets folder (note for the SQLiteAssethelper the file needs to be in the assets/databases folder).
The restriction/complexity/drawback is if the questions and answers changed over time (e.g. more questions and answers were added).
Another way could be to define the database structure and supply the questions as an external file rather than a pre-existing database (as an asset) which is read and used to insert the questions into the database. With some consideration/planning this could handle on-going questions, relatively simply.
Yet another way could be have the question on a centralised server (Firebase could be suitable for this) rather than an asset file. The App would connect to the server and load the questions into the database.
Add Android sqlite asset helper as a project dependency
dependencies {
compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
}
Create a database (with all required tables) on your PC using any SQLite database manager like SQLite Studio
Database created in step 2 would be a single file e.g (Quiz.db)
Now copy Quiz.db to your project assets/databases/ folder
Create DataBaseOpenHelper
public class DatabaseOpenHelper extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "Quiz.db";
private static final int DATABASE_VERSION = 1;
public DatabaseOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
Create DataBaseAccess class
public class DatabaseAccess {
private SQLiteOpenHelper openHelper;
private SQLiteDatabase database;
private static DatabaseAccess instance;
/**
* Private constructor to aboid object creation from outside classes.
*
* #param context
*/
private DatabaseAccess(Context context) {
this.openHelper = new DatabaseOpenHelper(context);
}
/**
* Return a singleton instance of DatabaseAccess.
*
* #param context the Context
* #return the instance of DabaseAccess
*/
public static DatabaseAccess getInstance(Context context) {
if (instance == null) {
instance = new DatabaseAccess(context);
}
return instance;
}
/**
* Open the database connection.
*/
public void openDatabase() {
this.database = openHelper.getWritableDatabase();
}
public SQLiteDatabase getWritableDatabase(){
return openHelper.getWritableDatabase();
}
public SQLiteDatabase getReadabelDataBase()
{
return openHelper.getReadableDatabase();
}
/**
* Close the database connection.
*/
public void closeDatabase() {
if (database != null) {
this.database.close();
}
}
}
Finally use it
DatabaseAccess databaseAccess = DatabaseAccess.getInstance(context);
databaseAccess.openDatabase();
String query = "select * from Questions order by QuestionID asc";
Cursor cursor = databaseAccess.getWritableDatabase().rawQuery(query, null);
// handle results here
databaseAccess.closeDatabase();
I want to create an offline dictionary app, that need sometimes to get update and old database will be replaced by new one. It's what I want to do, and I did something like this with SQLiteAssetHelper Library:
Note: SQLiteAssetHelper will copy database from assets folder into app data folder
public class MyDb extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "db.sqlite";
private static final int DATABASE_VERSION = 1;
public MyDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
now i want to update database, after puting new db.sqlite file into assets folder, I have manipulate my codes like this:
public class MyDb extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "db.sqlite";
private static final int DATABASE_VERSION = 2;
public MyDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
but when i compiled and run, it says: Can't upgrade read-only database from version 1 to 2
what is the solution?
by clearing app data, it will works fine...
SORRY FOR MY BAD ENGLISH...
Quoting the documentation:
If you have a read-only database or do not care about user data loss, you can force users onto the latest version of the SQLite database each time the version number is incremented (overwriting the local database with the one in the assets) by calling the setForcedUpgrade() method in your SQLiteAsstHelper subclass constructor.
You can additionally pass an argument that is the version number below which the upgrade will be forced.
Note that this will overwrite an existing local database and all data within it.
You are not calling setForcedUpgrade() from your constructor.
You should call setForcedUpgrade(); after your Constructor :
public class MyDb extends SQLiteAssetHelper {
private static final int DATABASE_VERSION = 2;//+1
public MyDb(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
setForcedUpgrade();
}
}
Please note that deleting the old db and creating new one in onUpgrade is not the right solution. Older data has to be migrated. Incase you are not interested in maintaining older data ov version 1, it is alright.
Right way is to use onUpgrade to modify the schema. Most of the time altering the schema required recreating table. In such cases follow below approach
rename existing table (say table_xyz) to be altered to some temp
name (say temp_table_xyz)
create the table (table_xyz) with new schema
copy the contents from temp table temp_table_xyz to the
new table_xyz
It's solved by this:
#Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion != newVersion) {
context.deleteDatabase(DATABASE_NAME);
new MyDb(context);
}else
super.onUpgrade(db, oldVersion, newVersion);
}
It's very useful for some database that we don't need old data when getting update.
I am using SQLiteAssetHelper dealing with database to realize selection, inserting and updating. But I need more than one database; can I put all the databases' method in one class? I tried, but the error is cannot find my second table. Thank you.
What do you mean by all the databases method in one class?
You can't have a single Helper class for multiple databases. Each database is represented by one single Helper. Then, you can create another class that takes an helper in its constructor and create methods using it.
EDIT
For each table you want to use, you need to create a class that extends SQLiteAssetHelper.
public class MyDatabase extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "northwind.db";
private static final int DATABASE_VERSION = 1;
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
Use a different name for each table in the DATABASE_NAME variable.
Then use the helper corresponding to the table you want to read or write to as normal. There is samples here: http://developer.android.com/training/basics/data-storage/databases.html#ReadDbRow
I have a SQLite database in my application. I access it using a SQLiteOpenHelper with a static factory, so that only one helper instance exists at a time. I'm not entirely sure why this is necessary; I did it because I read there was some opportunity for leaks if multiple helpers were created:
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper sInstance = null;
public static final String DATABASE_NAME = "score_database.db";
private static final int DATABASE_VERSION = 1;
public static final String SCORE_TABLE_NAME = "testTable";
public static final String KEY_NAME = "scorekey";
public static final String SCORE_NAME = "score";
private static final String SCORE_TABLE_CREATE =
"CREATE TABLE " + SCORE_TABLE_NAME + " (" + KEY_NAME + " INTEGER PRIMARY KEY, " + SCORE_NAME + " INTEGER);";
/**
* Constructor for DatabaseHelper class.
* #param context
*/
private DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* Gets the database helper instance (if an instance already exists, it will be retrieved).
* #param context The calling context
* #return The database helper instance
*/
public static DatabaseHelper getInstance(Context context) {
if(sInstance==null)
sInstance = new DatabaseHelper(context);
return sInstance;
}
/**
* Creates the necessary database structure for score storage.
* #param db The database object
*/
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SCORE_TABLE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
I have to access it without locking up the UI thread; as such, I only call getWritableDatabase from inside an AsyncTask. When the access is complete, I close the database that getWritableDatabase returned.
Now, sometimes, my app will start another AsyncTask of the same type, to perform another database access, while one is already running. I can't access the database at the same time in multiple threads, so I have a lock object shared by all the AsyncTask objects that queues up the database operations (this works fine). The problem is, once in a blue moon, the following occurs:
The app starts an AsyncTask that locks the database and starts accessing it.
The user quits the app (the task keeps running in the background; even if I cancel it, there's no stopping it if I am in the middle of a database access).
The user opens the app again and starts another AsyncTask. Because the lock was a field in the activity, this new AsyncTask acquires the lock in the new activity instance, and accesses the database while the old task is still running. Then an error occurs when one of the tasks closes the database while the other is trying to work with it.
The behavior is intermittent, but only because of the low chance that these conditions will be met. I don't want this to occur for a user, ever. Basically, I either need a way to let multiple tasks access the database simultaneously (eliminating the need for a lock) or a way to have a lock carry across multiple activity instances as the app is destroyed and recreated.
Anybody have any suggestions?
As a rule of thumb, I'd rely on Android OS as much as possible. That said, why won't you use Content Provider for DB operations? It somewhat handles multithreading/transactions/locking DB for you.
As alternative (and this solution is not smell pretty well) you can back up your lock field in SharedPreferences, as this is stored in a file, it will preserve it over application's death and rebirth, just restore it onCreate.
SQLiteOpenHelper is modeled so that it caches the SQLite connection for reusing. There is no need to ever manually close the connection in this case, although the helper will automatically reopen a new connection on the next access, so if the access is kept serialized it will still work.
In my experience, SQLite transactions are serialized by the framework, so if you use the above model, then you don't need to serialize them yourself.
Also, to comment on your serialization scheme, you should have kept the shared lock static at the same level as the AsyncTasks (preferably using the SQLiteOpenHelper object itself as the lock). Also you can specify a serial executer in the executeOnExecutor() method, which was the default pre-Donut and was again reverted to be the the default post-Honeycomb.
Is it possible (simple) to get the current database version number, increment it, and pass it to SQLiteOpenHelper's constructor from within an activity rather then hard coding the version number like so?
Activity (called from onclick):
public void updatedb(){
//pseudo-code next 2 comments
//int incVersion = getCurrentDBVersion();<==question here
//incVersion = incVersion + 1;
DatabaseHandler dbincrement = new DatabaseHandler(this, incVersion);
}
SQLiteOpenHelper Extension:
public class DatabaseHandler extends SQLiteOpenHelper{
public DatabaseHandler(Context context, int incVersion) {
super(context, DATABASE_NAME, null, incVersion);
}
}
Yes, use something like this to get the database version:
DatabaseHandler dh = new DatabaseHandler(this);
SQLiteDatabase db = dh.getWriteableDatabase();
int version = db.getVersion();
DatabaseHandler dhInc = new DatabaseHandler(this, version++);
You cannot get the current db version number unless you first open the database. So if your intention was to get the installed database version, increment it, and then call the SQLiteOpenHelper constructor with the new version than I don't believe what Sam proposed will satisfy your request.
Here's why:
1) the first line in a DatabaseHandler constructor must invoke the SQLiteOpenHelper (super) constructor, providing your database version.
2) invoking getWriteableDatabase automatically causes the invocation of the onOpen and/or onUpgrade/onDowngrade methods in your DatabaseHandler. Which onXXXX method is called depends on SQLiteOpenHelper's comparison of the installed database version to the version you provided in your constructor initialization above.
I don't know an easy way to find out the installed database version without providing one first.