I'm running into a very frustrating bug.
I have a java class that reads in data from a file, and enters it into the database.
package edu.uci.ics.android;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DbAdapter extends SQLiteOpenHelper{
private static final String DATABASE_NAME = "mydb";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "fruits";
private static final String FRUIT_ID = "_id";
private static final String FRUIT_NAME = "name";
private static final String FILE_NAME = "fruits.txt";
private static final String CREATE_TABLE = "CREATE TABLE "+ TABLE_NAME + "("+FRUIT_ID+" integer primary key autoincrement, "+FRUIT_NAME+" text not null);";
private SQLiteDatabase mDb;
private Context mContext;
public DbAdapter(Context ctx){
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
mContext = ctx;
this.mDb = getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
// populate database
try {
BufferedReader in = new BufferedReader(new InputStreamReader(mContext.getAssets().open(FILE_NAME)));
String line;
while((line=in.readLine())!=null) {
ContentValues values = new ContentValues();
values.put(FRUIT_NAME, line);
db.insert(TABLE_NAME, null, values);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
onCreate(db);
}
public Cursor fetchAll() {
return mDb.query(TABLE_NAME, new String[] {FRUIT_NAME}, null, null, null, null, null);
}
}
EDIT:
To be more clear, this is what fails:
When I change the database name variable, the table name variable, the program force closes, indicating that something went wrong. Why can't I change the name of the table I create?
When I make changes in the fruits.txt file, I don't see anything reflecting those changes at run-time. Why does this happen?
SQLiteOpenHelper.onCreate() will only get called automatically if the database does not exist, which will only happen once. After that, the database file exists on the device's internal storage so it is simply going to load up the version it has.
If you want to create a new database when the external file is changed, you need to either delete the current database file (manual process) or also change the current database version the helper is created with. When Android sees that the version SQLiteOpenHelper is created with varies from the current file in internal storage, it will call onUpgrade() to allow the existing database to be modified to match the new "version".
EDIT:
To clarify, when you create a database, a db file is created (and persisted) on the device's internal storage, separate from your application's assets. When you re-run your application, persisted data storage does not clear out (or else it wouldn't be "persisted" anymore) so that database file from the last run of your application still exists...with all the settings from when it was created.
When you make changes to the variables in this class, it doesn't somehow magically modify the database file that already exists on the device, so now you are looking for tables and databases that don't exist (probably where your crashes are coming from).
If you simply need to clear out the database so it will reflect changes you've made during development, just clear the database manually on the device by going into Settings -> Manage Applications -> {Application Name} -> Clear Data. This deletes persisted files so they can be re-created by your application the next time you launch it.
If, however, you need this to somehow be a feature where your application automatically recognizes changes you've made to a file in /assets and modifies or re-creates the database as a result, then look at my previous suggestions about using the upgrade mechanism built into SQLiteOpenHelper
HTH
When you change the database name or table name in your code, they no longer reflect the names in the database on the device, so you get a force close. During development, the easy thing is to just uninstall your application and then reinstall whenever you make incompatible changes like that. When changing your database schema from one released version to another, you need to increase the database version number and do the right thing in onUpgrade().
For example, right now, you have
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
onCreate(db);
}
So when you change the table name in code from, say "fruits" to "veggies", onUpgrade() gets run, but table veggies doesn't exist, so it isn't dropped, and then you call onCreate(db) whith a conflicting shchema on top of the existing database. So you need to check oldVeresion and newVersion and do something more like
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion == 2 && oldVersion == 1) {
db.execSQL("DROP TABLE IF EXISTS" + TABLE_NAME_V1);
db.execSQL("CREATE TABLE "+ TABLE_NAME ...);
}
}
If you're trying to change fruits.txt on the device, it won't work. That's how Android is designed: your assets never change, they're a read-only part of your APK. You need to write the fruit.txt file to the SD card if you want it to be able to change it.
Related
I would like to create a database in cache so it could be wiped out when user clicks Clear cache button.
In SQLiteOpenHelper constructor there is only argument to pass a name of database, not a directory (default is dadabases).
Is there any option to delete such DB when user wants to clear cache?
Here is an example of creating a database in your cache directory:
public class CachedDatabase extends SQLiteOpenHelper {
public CachedDatabase(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, new File(context.getCacheDir(), name).getAbsolutePath(), factory, version);
}
#Override public void onCreate(SQLiteDatabase db) {
// just for testing
db.execSQL("CREATE TABLE example (id INTEGER PRIMARY KEY AUTOINCREMENT, foo, bar, baz, qux)");
}
#Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
All you need to do is pass the absolute path as the name parameter in the constructor.
Tested just to make sure it was created in /data/data/[package_name]/cache:
CachedDatabase cachedDatabase = new CachedDatabase(this, "TEST", null, 1);
SQLiteDatabase database = cachedDatabase.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("foo", "foo");
values.put("bar", "bar");
values.put("baz", 123);
values.put("qux", true);
database.insert("example", null, values);
Checking for the database:
$ adb shell
$ run-as [YOUR_PACKAGE_NAME]
$ ls cache
TEST
TEST-journal
You can create and open a database in a specific location, but you need to use SQLiteDatabase class (choose one of the openOrCreateDatabase methods): as you mentioned you can't create a database in a different path using the provided SQLiteOpenHelper.
You can also modify the code of the SQLiteOpenHelper to match your needs. Take the source code from the link, copy in a new class of your project and modify the getDatabaseLocked method.
You can also take inspiration from SQLiteAssetHelper.
If you are looking to clear the data in tabledb.execSQL("delete * from "+ TABLE_NAME);
you can also delete the tables db.delete("TABLE_NAME", null, null);
I am building an Android app that includes a built in database, and I am regularly testing it on my Samsung Galaxy S3 device.
I am having an issue now however, since I noticed that the SQLite database on the app is not updating even after I make changes to it on my computer using SQLiteBrowser.
I have verified that I am indeed writing the changes to the database and then rerunning the app from scratch on the device, but still the query results when performed by the app are unchanged, despite the changed data.
I've tried updating the version of the database, but this gives me all sorts of errors because it wants me to write an update script which I don't think is necessary for what I need to do.
Does anyone know how to refresh the data in the database on the device?
The easiest way to fix this is to go into Application Manager, select your app and then tap "Clear Cache". This will delete all the original databases that the app created. Android devices maintain this cache to persist databases even after the original app was uninstalled (for a few reasons and when they are deleted depends on the manufacturer's firmware). It is because of this, that even though you see that you are creating your new database, android is still querying the old ones.
You can then do a fresh install of your app and this should fix the problem. Outside of this, your only option is to write an upgrade script within the onUpgrade() method which does the upgrade.
You might not be writing to the database that is on the device, but in the folder on your computer. You can try having the app itself do some test updates to the database so that you know it is updating the SQLite Database on the device:
public class SQLHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "name goes here";
private static final String[] KEYS = {"column","names","go","here"};
private static final String DICTIONARY_TABLE_NAME = "tablename";
private static final String DICTIONARY_TABLE_CREATE =
"CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +
KEY[0] + " TEXT, " +
KEY[1] + " TEXT, " . . .;
public SQLHelper(Context context) {
super(context, DATABASE_NAME , null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// creates table if does not exist
db.execSQL(DICTIONARY_TABLE_CREATE);
// . . . update database with test info here...
db.execSQL("INSERT INTO . . .");
db.execSQL("UPDATE . . .");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
You can use ADB to start a shell and use sqlite to directly act on the database http://developer.android.com/tools/help/sqlite3.html
Once you started sqlite3 just execute regular SQL commands
My goal is to build a custom version of SQLite (specifically with R-Tree enabled) to include in my Android project. The motivation stems from:
Android SQLite R-Tree - How to install module?
SQLite has some instructions in how to do this:
http://www.sqlite.org/android/doc/trunk/www/index.wiki
I have compiled SQLite and successfully included the shared library in my project. The problem I am having is that as soon as I call an operation such as getReadableDatabase() or getWriteableDatabase() I receive a SQLiteCantOpenDatabaseException.
It is important to note that I am using:
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteOpenHelper;
in place of:
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
In order to use the custom SQLite build through the NDK and JNI.
MySQLiteHelper:
public class MySQLiteHelper extends SQLiteOpenHelper {
static {
System.loadLibrary("sqliteX");
}
private static final String DATABASE_NAME = "mydata.db";
private static final int DATABASE_VERSION = 1;
Context myContext;
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
myContext = context;
}
public void getEntry(){
SQLiteDatabase db = this.getReadableDatabase();
db.close();
}
public void createEntry(){
// 1. get reference to writable DB
SQLiteDatabase db = this.getWritableDatabase();
//...
//...
db.close();
}
#Override
public void onCreate(SQLiteDatabase database) {
String create_rtree = "CREATE VIRTUAL TABLE demo_index USING rtree(\n" +
" id, -- Integer primary key\n" +
" minX, maxX, -- Minimum and maximum X coordinate\n" +
" minY, maxY -- Minimum and maximum Y coordinate\n" +
");";
database.execSQL(create_rtree);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + "demo_index");
onCreate(db);
}
}
Main Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MySQLiteHelper helper = new MySQLiteHelper(this);
helper.createEntry();
setContentView(R.layout.activity_my);
}
Stack Trace:
10-29 13:45:38.356 2360-2360/com.example.nathanielwendt.lstrtree E/SQLiteLog﹕ (14) os_unix.c:30589: (2) open(//arrrr.db) -
10-29 13:45:38.376 2360-2360/com.example.nathanielwendt.lstrtree E/SQLiteDatabase﹕ Failed to open database 'arrrr.db'.
org.sqlite.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
at org.sqlite.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
at org.sqlite.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:217)
at org.sqlite.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:201)
at org.sqlite.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:467)
at org.sqlite.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:189)
at org.sqlite.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:181)
at org.sqlite.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:809)
at org.sqlite.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:794)
at org.sqlite.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:699)
at org.sqlite.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:722)
at org.sqlite.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:228)
at org.sqlite.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:168)
at com.example.nathanielwendt.lstrtree.MySQLiteHelper.createEntry(MySQLiteHelper.java:65)
at com.example.nathanielwendt.lstrtree.MyActivity.onCreate(MyActivity.java:25)
I have seen several posts here on SO about this same exception, but they all seem to be related to importing the database from somewhere else on disk in which they do not set the file path correctly. This seems to be the most related post: Android SQLiteOpenHelper cannot open database file, but none of the suggestions worked.
I have tried this with 2 different phones and with the emulator and I receive the same error. Furthermore, I have replaced the imports discussed above with the default android sqlite imports and it works fine (given I don't try to create an R-Tree since it is not included in the default Android SQLite install). Changing permissions of database dir and .db file to 777 did not fix the issue, neither did adding WRITE_PERMISSION to my manifest.
This is an incompatibility in the SQLite Android bindings.
The original Android code opens the database like this:
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
while org.sqlite.database.sqlite.SQLiteOpenHelper does not use the context:
db = SQLiteDatabase.openOrCreateDatabase(
mName, mFactory, mErrorHandler
);
This means that the database path is not prepended to the database name.
Use something like this to get the complete path of the database:
String path = context.getDatabasePath(DATABASE_NAME).getPath();
and use that as the database name.
I have uploaded an application to play store couple of weeks back. This application involves sqlite database that stores information on username, password, other details that given are by user while using the application locally.
Now I have couple of more tables and fields added to database and wanna upload the application to playstore as an update?
My worry is if the user updates the application from playstore - After update - all the data stored in database will be saved or will the user has to recreate everything from scratch?
Let me know!
Thanks!
You have to override the onUpgade method of SQLiteOpenHelper. In the OnUpgrade method you can either erase the data(drop sqlite command) or maintain the data with the additional columns(alter sqlite command) or create new table (create sqlite command).
Refer the following snippet.
I assume your version would be 1.(Plz check the constructor of your SqliteOpenHelper class)
Increment the version by 1.
class DatabaseHelper extends SqliteOplenHelper{
private static final int DATABASE_VERSION = 2; //new version of the database
private static final int Database_name = "MyDatabase";
private static final String alterUserName = "alter table users add name text";
private static final String table_users = "create table if not exists "
+ users + "(" + "_id integer primary key autoincrement,"
+ "email text" + ")";
public DatabaseHelper(Context context) {
super(context, Database_name, null, DATABASE_VERSION);
cntxt = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(table_users);
db.execSQL(alterUserName);
}
}
Now everytime when you roll the next update with database changes be sure to increment the database version by 1 else let it remain the same.
This isn't done for you automatically. In your SQLiteOpenHelper, you need to increment the Schema integer. This will trigger the on upgrade method for your existing users.
Adding a table is not a problem, just do this in onUpgrade, nothing breaks.
However to add fields, you should use the 'ALTER TABLE' SQL command
If you add new columns you can use ALTER TABLE to insert them into a live table. If you rename or remove columns you can use ALTER TABLE to rename the old table, then create the new table and then populate the new table with the contents of the old tab
See the official reference here
I have read through a lot of the posts on copying the database file over from the assets or raw folders to the /data/data/APP/databases folder, but that would leave two copies of the DB on the device taking up valuable space. I am thinking of building a solution with a slightly smaller footprint and allowing for more flexible schema management by storing the database SQL creation text file in the raw folder and using the standard on_create / on_update process in the DBhelper class. However I am a little confused because the examples that copy the database over bypass the on_create and on_update methods.
Is this the recommended way if you are not building the db from strings in the code?
My solution would simulate the running scripts from code method by having the scripts all in one file. The reason I am looking at building the db this way is that my DB will have close to 100 tables when the application is complete, so I need the schema to be manageable.
Any guidance is welcome as I am still learning the best practices and patterns for Android.
Here is an example of my code:
public class DatabaseHelper extends SQLiteOpenHelper {
private final String DATABASE_NAME = "mydb";
private final int DATABASE_VERSION = 1;
private final Context myCtx;
private String DATABASE_CREATE_SCRIPT = null;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
DATABASE_CREATE_SCRIPT = getLoadFile();
// Create all tables and populate the lookup tables
db.execSQL(DATABASE_CREATE_SCRIPT);
db.execSQL(VIEW_CREATE_V_PERSON);
}
private String getLoadFile(){
InputStream inputStream = myCtx.getResources().openRawResource(resIdofmyfile);
InputStreamReader inputreader = new InputStreamReader(inputStream);
BufferedReader buffreader = new BufferedReader(inputreader);
String line;
StringBuilder text = new StringBuilder();
try {
while (( line = buffreader.readLine()) != null) {
text.append(line);
text.append('\n');
}
} catch (IOException e) {
// We have an error to look up
return null;
}
return text.toString();
}
/**
* onUpgrade will check the version of the database and update the database
* if a newer version is available.
*/
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Will set conditional upgrade checks
//Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which may destroy all old data");
db.execSQL("DROP TABLE IF EXISTS CONTEXT_LOAD");
db.execSQL("DROP TABLE IF EXISTS ISSUES");
db.execSQL("DROP TABLE IF EXISTS GOALS");
db.execSQL("DROP TABLE IF EXISTS PERSON");
db.execSQL("DROP VIEW IF EXISTS V_PERSON");
onCreate(db);
}
}
I guess it looks like a good practice. It's not very bad even if you create your database with code completely. Check out google's app for IO schedules it has a fairly big database creation part. It may lead you a way.
Here is the project home page, here is the database part
I use this application as a reference all the time it's like a work of art =)
Edit: Google updated the app so the old database link doesn't work.
I'm not really sure what your question is.
If you are asking if it's OK to create your DB from scratch using a text file containing a load of SQL CREATE statements then the answer is "yes".
Not only that but it's more flexible than distributing a pre-built DB within your APK as you could even download the 'schema' file or files from a network location. This allows you to update the schema dynamically at a central point.