Hi I am new to android and I have a problem in creating a database.
public class database extends ListActivity {
/** Called when the activity is first created. */
private final String MY_DATABASE_NAME = "myCoolUserDB.db";
private final String MY_DATABASE_TABLE = "t_Users";
Context c;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<String> results = new ArrayList<String>();
setContentView(R.layout.main);
SQLiteDatabase mydb=null;
try
{
mydb.openOrCreateDatabase(MY_DATABASE_NAME, null);
} catch(Exception e){}
}
}
When I run this code it throws a run time exception. Please help me.
If you are going to call a static method like openOrCreateDatabase, do it on the class (SQLiteDatabase.openOrCreateDatabase(...)), not an instance. It's a lot clearer - the way you've done it looks like you're calling an instance method, so looks like a sure NullPointerException, which of course is misleading.
As someone else has stated, the stack trace would be the most useful thing when asking for help with an exception.
(Almost) never catch an exception without at the very least logging it. Don't just do nothing with it. There are of course exceptions to every rule, but let's not go there for the moment. Anyway, if you don't at least log it, you're just throwing away information that would tell you what went wrong when everything goes to crap later.
You shouldn't be using that method directly, and should instead be extending SQLiteOpenHelper . See the android developers page on data storage to get started (I'd post a link but apparently I'm only allowed one link in my post ?!), and since you've probably had to download the SDK to get going, look in the samples that come with it for the Notepad sample application. That contains a NotePadProvider class, which is a good example of both a content provider and database access, which often go hand-in-hand on android. I'd suggest compiling that application and making some simple changes to it before you jump into making your own one.
For working with sqlite database you need to create class extended from SQLiteOpenHelper:
private class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLES);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(UPGRADE_TABLES);
}
}
Then you can get access to db using DbHelper object:
DBHelper dbHelper = new DBHelper(Activity.this);
SQLiteDatabase db = dbHelper.getReadableDatabase();
I run into the same problem. It figures out that two bugs happens during development
dir "databases" was not existent
accendently ".db" was created as directory.
They following code cover both
File dbFile = getDatabasePath ("abc.db");
if (dbFile.isDirectory ()) {
dbFile.delete();
}
if (! dbFile.exists()) {
String path = dbFile.getParent ();
new File (path).mkdirs ();
}
database = SQLiteDatabase.openDatabase (dbFile.getAbsolutePath (), this, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY);
Hope this helps
I think SQLiteOpenHelper is only useful for "single table" databases. For multiple table applications I consider directly using SQLiteDatabase fit better to a good architecture.
This is a simple post which tells you how to insert data in to a SQLite database in Android and further more this links shows you how to retrieve data from a SQLite database in Android .
Related
My app uses an SQLite DB, wrapped with a SQLiteOpenHelper and a ContentProvider. I added a sign-in feature to the app, and now I want every user to only be able to see his own data. The way I thought to achieve this is for the app to create a separate DB for every user that signs in to the app, and use the user's ID in the filename of the database.
I have this ContentProvider:
public class MyProvider extends ContentProvider {
//...
#Override
public boolean onCreate() {
dbHelper = new MyDBHelper(getContext());
return true;
}
I have this SQLiteOpenHelper:
public class MyDBHelper extends SQLiteOpenHelper {
Which has this constructor:
public MyDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
Up until now the app couldn't have multiple users, so it just had one database. so DB_NAME was always the same String. I now tried to set it like that:
private static String UID = FirebaseAuth.getInstance().getCurrentUser().getUid();
public static final String DB_NAME = String.format("data%s.db", UID);
(As you can see, I'm using Firebase Authentication)
but this resulted in a crash, because apparently the content provider is created on app start, before the user has authenticated. (so user is null. Yeah, I should check that user is not null before I try to call getUid(). but this won't make this thing work)
So this doesn't seem like the right approach. How can I use a different DB according to the signed user? Can I make the content provider to first be created after a user has authenticated?
I could also just keep everything in one database and add a UID column. But will this be protect the different users' data good enough from each other? Also, this would mean a lot more code changes.
How can I use a different DB according to the signed user?
The simple solution is to get rid of the ContentProvider. The only reason to use a ContentProvider is if you are going to be serving this data to other apps.
Also, I would be wary of just taking getUid() and putting it in a filename. You are not in control over what getUid() returns, and it might someday contain characters that are invalid in filenames.
Can I make the content provider to first be created after a user has authenticated?
No, sorry.
Seems that the right solution here is to not use ContentProviders. So I accepted the other answer.
But to answer my actual question, for people that are determined to make different DBs work with one ContentProvider, here is how it can be done:
I changed the custom SQLiteOpenDBHelper's constructor to also take a uid:
public MyDBHelper(Context context, String uid) {
super(context, String.format(DB_NAME, uid), null, DB_VERSION);
UID = uid;
}
and I changed the onCreate of my ContentProvider not to create the DBHelper. I created this function that initializes the DBHelper instead:
public void initDB(Uri uri) {
String uid = uri.getPathSegments().get(0);
if (dbHelper == null){
dbHelper = new MyDBHelper(getContext(), uid);
} else if (!uid.equals(dbHelper.UID)){
dbHelper.close();
dbHelper = new MyDBHelper(getContext(), uid);
}
}
and I call this method at the start of the query, insert, update and delete methods.
So the DBHelper which holds the open connection to the DB, is initialized whenever the content provider is preforming some action on the DB but there is either not yet an existing connection with the DB, or the connection is with a DB of a different user.
This is not the right way to solve this problem and this probably has consequences in some cases. But I didn't want to leave the question I asked unanswered.
I'm trying to implement version control into my database. I have following code:
if (File.Exists (dbPath)) {
database = new SQLiteConnection(dbPath);
dbVersion = GetDatabaseVersion();
if (dbVersion != DATABASE_VERSION) {
CreateDatabase(dbPath);
database = new SQLiteConnection(dbPath);
SetDatabaseVersion(DATABASE_VERSION);
}
} else {
CreateDatabase(dbPath);
database = new SQLiteConnection(dbPath);
SetDatabaseVersion(DATABASE_VERSION);
}
When I run the code for the first time File.Exists (dbPath) returns false as expected. When I then stop the app and build and deploy again, File.Exists (dbPath) returns true (as expected) and the versions get checked. But when I change an entry in the database (not the version) and I build and deploy again File.Exists (dbPath) returns false. So I guess the sqlite-file gets deleted when it's changed?
What I want to achieve is that when the sqlite-file is changed, but the version numbers are still the same, the database of the app isn't updated. Only when the versions don't match, it has to be updated.
(It works perfectly for iOS this way, by the way)
Can anyone help me?
Thanks in advance.
It's difficult, without access to your debugger, to answer what's going wrong. However, your way of creating and updating your database looks unusual and unnecessarily complicated. You should consider using SQLiteOpenHelper, which will allow you a structure like this:
public class MyDatabase extends SQLiteOpenHelper {
private static final int VERION = ...;
public MyDatabase(Context context) {
super(context, "mydatabasename", null, VERION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table...");
...
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, intNewVersion) {
db.execSQL("...") // whatever you have to change from one version to another
}
}
This works without losing any database files.
You should tick this box in Visual Studio / Tools / Options / Xamarin / Android settings
I don't know how to handle this correctly without getting database locked errors.
My app basically downloads many items in batches of 100 rows (~ 60.000 items) and inserts them in the database. Each batch of 100 rows is processed into a transaction.
The main activity allows the user to navigate between screens (fragments) while records are being downloaded and inserted. Most of the other screens contains read data from the database. I get a lot of database lock errors during reading. All readings are done in the main activity (not fragments) in different async tasks
So far I just used the "classic approach"
public class DBAdapter {
public DBAdapter(Context ctx) {
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE_TABLES);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Utils.log("Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
onCreate(db);
}
public DBAdapter open() throws SQLException {
database = DBHelper.getWritableDatabase();
return this;
}
public void close() {
DBHelper.close();
}
Then on my activity's onCreate() I call db = new DBAdapter(context); and each time I am doing an database operation (read/write) I call db.open() and after the insert/read is done I call db.close()
My questions are:
what would be the best approach to this situation ?
Considering I do a lot of write/read would it be better to call db.open on onCreate and db.close() on onDestroy() ? Would this be better than calling open/close each time I need to access the database ?
What do I need to do to avoid database locking on reading ?
I had a exactly similar situation like yours. In addition to what you described, in my app the user also can update the database through input on the screen.
The way I resolved it ( I don't know if it's the best way, but I hardly see any locking issue now)
Make a singleton class derived from SQLiteOpenHelper to make sure only one instance is running at any given time.
Implement ContentProvider class for insert/update/delete/query operations. Make all those functions 'synchronized'
Only close the db in ContentProvider's shutdown function. I do a very frequent db operations, so I don't want to open/close everytime. But I am not sure if it's the correct way of handling it.
Do access DB only through ContentProvider interface from anywhere
A very simple approach, or maybe a workaround is using synchronized methods for opening and closing the database object. I don't really know if it's the best practice, but at least it's simple and easy. Add this methods to your DBAdapter Class, and use them instead of db.open and db.close. The use_count attribute simple holds how many times open has been called. Initialize it with a value of 0. Also, in order to make it work on your solution be sure to pass the same DBAdapter object between the fragments. Don't create a new one everytime :
private int use_count = 0;
public synchronized void doOpen()
{
use_count++;
this.open();
}
public synchronized void doClose()
{
use_count--;
if (use_count == 0)
{
this.close();
}
}
Consider wrapping the SQLite database in a ContentProvider and using CursorLoader to do the queries from the various activities & fragments. This isolates the management of the database from the Activity/Fragment life cycle and can result in many fewer open/close cycles.
You may still run into contention between the reads and writes, but having all the database interaction in the same module should make it easier for you to address these issues.
Some interesting links: http://www.vogella.com/articles/AndroidSQLite/article.html#todo
When to use a Content Provider
I am looking for best approach to handling database connection. I have designed my application base on following method and works fine. However, sometimes application crashes especially on old model devices. Please look at my following method.
I have a DatabaseHelper class which all of tables will be created here. This class is like this:
public class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.i(TAG, "Object created.");
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CRT_TBL_1);
db.execSQL(CRT_TBL_2);
db.execSQL(CRT_TBL_3);
db.execSQL(CRT_TBL_4);
db.execSQL(CRT_TBL_5);
db.execSQL(CRT_TBL_6);
db.execSQL(CRT_TBL_7);
db.execSQL(CRT_TBL_8);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(DatabaseHelper.class.getName(), "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data.");
onCreate(db);
}
}
As you have seen, I have 8 tables which provides data for 8 different activities. Implementation of handler class for one activity is like following code.
public class DatabaseHandler_1 {
private final String TAG = "DatabaseHandler_1 ";
private DatabaseHelper dbHelper;
private SQLiteDatabase database;
public DatabaseHandler_1 (Context context) {
dbHelper = new DatabaseHelper(context);
Log.i(TAG, "Object created.");
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
// ... Other methods insert, update, query, ...
}
Finally, based on required information one or some of them will be used in each activity.
public class Activity_1 {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "Try to create activity...");
// Creating Database/Table
dbHandler_1 = new DatabaseHandler_1 (this);
dbHandler_1 .open();
dbHandler_2 = new DatabaseHandler_2 (this);
dbHandler_2 .open();
dbHandler_3 = new DatabaseHandler_3 (this);
dbHandler_3 .open();
}
#Override
protected void onDestroy() {
super.onDestroy();
// Closing database
dbRecipesHandler.close();
dbFavoritsHandler.close();
dbShoppingHandler.close();
}
// Other methods for displaying data...
}
When crashes happens logcat shows the connection is close while my application tries to query in database. I think scenario is like this:
User opens 1'st activity then he goes to second activity and then he goes to third activity. Now, if user clicks on back button third activity will be closed and onDestroy() method of this activity closes database connection. Because, second activity is still in memory, activity assumes that the connection is still open and when it wants to query into database, crash happens.
Logcat shows a message like this: Database connection is close. You wanted to query...
One solution is instead of letting Android OS handles database connection, we manually open connection and use it the manually close it.
Second solution is in Handler class manually open and close connection for each method.
What is you suggestion? did you have any experience about it?
Any suggestion would be appreciated.
In general I would suggest you consider wrapping your data with Content Providers then using Loaders in your activities to mingle data with the UI. It might seem like a lot of work, but among the many benefits of content providers you will not have to manage database connections or cursors within your activities. (Note, your DatabaseHelper class would remain the same)
Having done data both your way as well as with content providers in the different apps I've made, I would say content providers are almost always the way to go. It's always ended up being more maintainable and extendable in my experience, plus you get a lot of baked in features that would be really expensive to build yourself.
That said, if you really prefer the more direct approach you're currently using, I would at the least not tie the opening and closing of your connections to the activity's life cycle. Only open those connections at the instant you actually need data, get that data, then immediately close any cursors along with the connection itself.
Finally I solved the problem. I thought that I should open database one time in activity. I tried to open database again in onResume() method of each activity and closing it in onPause() of each activity instead of closing in onDestroy() method.
So far problem solved. Hope to don't see it again. If i face it again then will update/remove answer.
I am designing an application for an online exam. And I created a database using SQLite Browser and pulled it to Eclipse. In an emulator its working fine; it is able to retrieve and store data. But the problem comes when I place the .apk file on the mobile. On the mobile it's unable to retrieve the existing database. I am unable to bind the database file along with the .apk file, even after placing it in the assets folder.
Can anyone help?
Details:
Registration module
User Test module (display the questions from database)
Score submission module
How are you querying on database ?
The correct way is not to place a separate db in your code, but to create one dynamically. For e.g. the following code :
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSql("/*Put Create Table sqls here*/");
//onCreate will be called only once( when db doesn't exists for application, it creates here with the code)
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Use a database helper class like this in your code.
Now whenever you want to query on your db you can do it like this :
dbHelper = new DatabaseHelper(ctx);
db = dbHelper.getWritableDatabase();
//Start querying on db.(if it is not created oncreate() of dbhelper will create it for you.
So simply put your initial db create,/insert statements in oncreate() of dbhelper