I read that it was better to use only one SQLiteOpenHelper instance in a class extending Application:
public class MyApplication extends Application {
private static SQLiteOpenHelper dBHelper;
#Override
public void onCreate() {
super.onCreate();
dBHelper = new DBHelper(this);
}
public static SQLiteDatabase getDB() {
return dBHelper.getWritableDatabase();
}
}
but in that case, where should I close it?
Thanks
Jul
You should always have one and only one SQLOpenHelper instance and you should never close it. More detailed explanation can be found here and here
You may close the database in the onTerminate() method of the Application subclass. If your application can handle a closed database (by reopening ...) you can additionally close in the onLowMemory() method (but this will not free much memory).
More important is that you close any database cursors if you are finished using them.
EDIT
vmirnonov is right, onTerminate() is only called in the emulator, so ignore that part of the answer.
Related
I am creating an application which makes a lot of interactions with a database (both read and write operations).
To avoid open/close operations at each request, I created a class extending SQLiteOpenHelper with a Singleton design pattern. This way, I am sure only one instance of the SQLiteOpenHelper and only one connection to the database is made during all the application lifecycle (and not only activity lifecycle).
I also read some articles about ContentProvider, but I am not sure it's a better way.
So, this is the main logic of my Singleton class (onCreate and onUpgrade removed) :
public final class BaseSQLite extends SQLiteOpenHelper {
private static BaseSQLite mInstance = null;
private SQLiteDatabase db = null;
public static BaseSQLite getInstance(Context context) {
if (mInstance == null) {
mInstance = new BaseSQLite(context.getApplicationContext(),
DBNAME, DBVERSION);
}
return mInstance;
}
private BaseSQLite(final Context context, final String name,
final int version) {
super(context, name, null, version);
db = getWritableDatabase();
}
#Override
public synchronized void close() {
if (mInstance != null)
db.close();
}
public Cursor getAllData() {
String buildSQL = "SELECT * FROM myTable";
return db.rawQuery(buildSQL, null);
}
}
So, to access my database, I made this :
BaseSQLite baseSQLite = BaseSQLite.getInstance(context);
baseSQLite.getAllData();
It works perfectly for now. But my question is about the close() method. I really don't know when to call it. Actually, my database instance is the same for every Activies of my application, so I think it's a bad idea to call close() in an onPause() method, because the instance will be potentially (and it will often happens) recreated in the onStart() method of the next Activity. Also, I can't detect the end of my application, i.e. when no activity is visible on the screen anymore.
Can somebody give me some help about this issue ? I found some answer when the database is linked to ONE activity, but no really hint is given for my case.
You should call close anytime you are done writing to your database. For example when you insert data, you will have an open connection to the database that should be closed when it is done.
Reading is different. When you create a SQLite database on your phone, the data is persistent. The database exists and the handler you create provides a convenient way to access that information. Reading the database usually takes place by getting a readable instance of the database and using a Cursor to extract values. In that case you close the cursor when you're done, not the database itself.
You're right that you should not be closing the database connection during separate activities' lifecycle methods. Instead, as suggested above, close the database connection in your handler's methods that write to the database when you are done performing that transaction.
I want to check if a SQLite Database is open, and if it is, I would like to access that Database within a service class.
I am worried, and have seen, that multiple open calls to the database clash, and throw exceptions. Because I do query the database within both my Activity and Service classes, I am attempting to implement the solution Commonsware recommended here: When to close db connection on android? Every time after your operation finished or after your app exit. However I do not want to close then open the Database within the Service class if the Activity might need it. From this answer Why use SQLiteOpenHelper over SQLiteDatabase?, it looks like it might make sense to implement a SQLiteOpenHelper to solve the issue of making multiple calls.
Thank you so much for all your help!!
This man Kevin is a legend: http://touchlabblog.tumblr.com/post/24474750219/single-sqlite-connection. Thank you so much.
On that link he shares his ridiculously simple solution:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static DatabaseHelper instance;
public static synchronized DatabaseHelper getHelper(Context context)
{
if (instance == null)
instance = new DatabaseHelper(context);
return instance;
}
//Other stuff...
}
Then in my SQLite class I changed my code to look like this:
public BlacklistWordDataSource(Context context) {
dbHelper = MySQLiteHelper.getHelper(context);
}
at onCreat in the activity put datasource.open();
then do what ever you want
and at the end of actevity put this to close :
/** Call after close activity */
#Override
protected void onStop() {
super.onStop();
//Close dataSource
datasource.close();
}
So I've come up with some idea and I'm wondering if it is realizable.
Let's say I've multiple tables(database models) each of them is represented by some class.I don't wont to use singleton pattern with the open helper so I've created some simple class to provide single instance of the database.My idea is that as long as all tables hold reference to SQLiteDatabase(returned by the open helper) they will all work with the same DB instance and probably won't be needed to synchronized the work with the database since the open helper do this.When the last table finish it's work the GC will collect the open helper (since the last reference will be weak reference) -> finalize() is called and I close the db during this method to prevent any warning from OS. My question is: Is this could work?Will it close automatically the DB and will it leak or throw some exception?
Here is my class:
public class DatabaseHelper {
private static WeakReference<SomeCustomOpenHelper> sDBOpenHelper;
private void notifyDBCreate(SQLiteDatabase db) {
for (DBTable table : mTables) {
table.onDBCreate(db);
}
}
private void notifyDBUpgrade(SQLiteDatabase db) {
for (DBTable table : mTables) {
table.onDBUpgrade(db);
}
}
public SQLiteDatabase getDatabase(boolean readOnly) {
SomeCustomOpenHelper dbHelper = sDBOpenHelper.get();
if (dbHelper == null) {
dbHelper = new SomeCustomOpenHelper(context, name, factory, version, new DatabaseEventsCallback());
sDBOpenHelper = new WeakReference<SomeCustomOpenHelper>(dbHelper);
}
if (readOnly) {
return dbHelper.getReadableDatabase();
} else {
return dbHelper.getWritableDatabase();
}
}
private class DatabaseEventsCallback implements IDatabaseEventsCallback {
#Override
public void onCreate(SQLiteDatabase db) {
notifyDBCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db) {
notifyDBUpgrade(db);
}
}
interface IDatabaseEventsCallback {
void onCreate(SQLiteDatabase db);
void onUpgrade(SQLiteDatabase db);
}
private static class SomeCustomOpenHelper extends SQLiteOpenHelper {
private IDatabaseEventsCallback mCB;
public SomeCustomOpenHelper(Context context, String name, CursorFactory factory, int version, IDatabaseEventsCallback cb) {
super(context, name, factory, version);
mCB = cb;
}
#Override
public void onCreate(SQLiteDatabase db) {
mCB.onCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
mCB.onUpgrade(db);
}
#Override
protected void finalize() throws Throwable {
this.close();
super.finalize();
}
}
}
Did not really know the answer neither, but got interested and looked it up.
The answer is written out properly here;
http://blog.foxxtrot.net/2009/01/a-sqliteopenhelper-is-not-a-sqlitetablehelper.html
But basically the core of the info is;
I created three SQLiteOpenHelper classes, one for each table, even though they all referenced only a single database file.
Here is where everything fell apart. Android maintains Versions for databases based on the package it’s associated with, the name of the database, and the version number you provide. The package and name go into decided what the path on the device will be, while the version is stored (somewhere) on the device so that it knows when it needs to call an OpenHelper’s onUpgrade event handler. It turns out that if, in the SQLiteOpenHelper Constructor, it determines that the database already exists, it won’t call your onCreate or onUpgrade methods at all, even if the particular class which is making the call has never been called before.
I've been through the same issue when I was working on a project. I also went crazy on the doubt if the static instance was using enough memory and causing a considerable memory leak.
I'm not sure if creating a weak reference would guarantee that database instance would be collected. However a possible workaround could be : Assigning a null value to static database instance once your all database transaction is done and you've close the database. This might ensure that the database instance no more allocates any memory.
Let me know if this works or if there is a better work-around.
You can do so. As you say the locking should be happening on the SQLite and I've never heard issues around that so you should be fine with this.
The only restriction you have is that all the tables will have to go into the same database since Android for now just allows you to have one file.
Closing the database is a different thing, that's why it is actually interesting to use the singleton pattern (you avoid closing + opening all the time).
Nonetheless with your approach you just need to make sure to close the db whenever you are done with it. As far as I'm concerned this is not automatically done.
Additionally Lars Vogel has written extremely useful and detailed articles around DB access in Android. You might want to have a look there. http://www.vogella.com/articles/AndroidSQLite/article.html
you can use one open helper for all the table .i am using the single instance in my app also like this .
public static synchronized DatabaseHelper getInstance(Context ctx)
{
if (dbhelper == null) {
dbhelper = new DatabaseHelper(ctx);
}
return dbhelper ;
}
My question is: Is this could work?Will it close automatically the DB
and will it leak or throw some exception?
NO it will not close automatically database , when your application will demand for DATABASE object and OS found some of your database instant are alive then Android framework try to connect that object reference (which is probably weak reference )
and i have to say , I don't recommend opening and closing a DATABASE on-demand or temporarily . It is always nice to open the DB up early and keep it open for the duration of your whole activity and close it when the activity gets finished or suspended .
I have an app which stores some data in a SQLite DB.Also I'm doing a lot of query an requery in my app.I have about 15 activities in it.And almoust all use the DB to query for data.
But what I'm doing is opening my DB in every activity and close it in onDestroy{...} of every activity.
The problem is that onDestroy{...} may never get called and sometimes my app stays on for a long time and I switch from an activity to another opening for to many times my DB.
And sometimes I get errors like-database too many times opened and never closed.
I would try to close my DB exactly after I get my data from it and close it...moving to my next activity and re-opening and so on.....
But the problem is that in some activities I come back from other activities....closing my DB and coming back to that activity would produce a big Force Close.
What I wanna do is open my DB at the beginning of my app and close it at the end of it, but I'm facing 2 problems:
1.
Should I make my SQLiteOpenHelper class a singleton?...get an instance of it..open it in my first activity and then in my following activities just get an instance of my DB which is already opened/???
2.Where is the end of my app????How should I know where is the end of my app and where to close my DB.
EDIT:
public class DBAdapter extends SQLiteOpenHelper {
public DBAdapter(Context context) {
super(context, DATABASE_NAME, null, 1);
this.myContext = context;
}
public void openDataBase() throws SQLException {
String myPath = DATABASE_PATH + DATABASE_NAME;
db = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
}
}
That is a piece of code from my class that manages my DB.To make this singleton I should use a constructor likw this:
private DBAdapter()
{
//nothing in here
}
But this is undefined for SQLiteOpenHelper
EDIT FINAL:
This is how I did it according with zirael advice:
package com.Server_1;
import android.app.Application;
public class MyApplication extends Application{
private static DBAdapter db;
public void onCreate()
{
db=new DBAdapter(getApplicationContext());
db.createDatabase();
db.openDataBase();
}
public static DBAdapter getDatabaseAdapter()
{
return db;
}
}
In every activity I where I need DB connection I do this:
MyApplication myApplication = (MyApplication) this.getApplication();
DBAdapter db= myApplication.getDatabaseAdapter();
And finally my manifest looks like:
<application android:icon="#drawable/icon"
android:label="#string/app_name"
android:name=".MyApplication"
android:debuggable="true">
In my app I open connection to database in myApplication class (your custom class that extends Application - it should be named the same as your application in androidManifest).
AndroidManifest.xml
<application android:label="#string/app_name"
android:name="com.mypackage.MyApplication " android:debuggable="true">
MyApplication .java
public class MyApplication extends Application {
private DatabaseAdapter dbAdapter;
#Override
public void onCreate() {
dbAdapter = new DatabaseAdapter(getApplicationContext());
dbAdapter.open();
}
And in each class that need db connection I simply use:
MyApplication myApplication = (MyApplication) this.getApplication();
DatabaseAdapter dbAdapter= myApplication.getDatabaseAdapter();
MyApplication is run automatically on every application start. This way I keep only one connection to DB so it's closed when app is being removed from memory without any problem.
When you retrieve your dbAdapter from you MyApplication class, do it in a lazy fashion, creating it only when needed. In my implementation, I also open it at this time.
public static DbAdapter getDbAdapter() {
if (dbAdapter == null) {
dbAdapter = new DbAdapter();
}
dbAdapter.open();
return dbAdapter;
}
It is a good idea to use getReadableDatabase() or getWriteableDatabase() in the open method of your database adapter.
Also, I think it works better to retrieve your adapter in onStart() and close it in onStop() of the activities where it is being used, rather than using onCreate() and onDestroy().
#Override
protected void onStop() {
super.onStop();
MyApp.closeDatabase();
}
And in the MyApp class...
public static void closeDatabase() {
dbAdapter.close();
}
If you are not managing your database in line with Google's recommendations, why not - there's usually a good reason why things are the way they are...
In any case, you can use getReadableDatabase() and getWriteableDatabase() - these functions will open the database if necessary, but just return the existing database object if it is already open, thus preventing you from opening the database multiple times.
Something you might try is use a singleton that each activity would attach to in its onResume() callback, and detach from in its onPause() callback. When the detach count reaches zero, set a timer which would get canceled in the attach method. If the timer expires, close the database.
There is a good answer from another question Best place to close database connection
"According to this post by a Google engineer, there's nothing wrong with leaving the database connection open:
Android made a deliberate design decision that is can seem surprising,
to just give up on the whole idea of applications cleanly exiting and
instead let the kernel clean up their resources. After all, the kernel
needs to be able to do this anyway. Given that design, keeping
anything open for the entire duration of a process's life and never
closing it is simply not a leak. It will be cleaned up when the
process is cleaned up.
So, for simplicity, I would extend the Application class to provide a single well-defined entry point for your code, and open the database connection in its onCreate(). Store the DB connection as a field in your Application, and provide an accessor method to make the connection available to rest of your code.
Then, don't worry about closing it."
Hey! I want to use a singleton class, because if I open the database every activity I get "Leak found"( that happens because I open the database even if it is already open ) . I create a singleton class , but I don't know how should I use it.
Here is my class:
package com.ShoppingList;
import com.ShoppingList.databases.DbAdapter;
public class DbManager {
DbAdapter db;
// singleton
private static DbManager instance = null;
private DbManager() {
}
public static DbManager getInstance() {
if (instance == null)
instance = new DbManager();
return instance;
}
public void setinstance(DbAdapter db){
this.db=db;
}
public DbAdapter getinstancedb(){
return db;
}
}
In the first activity I put :
db = new DbAdapter(this);
db.open();
DbManager.getInstance().setinstance(db);
and for the next activity : DbManager.getInstance().getinstancedb(); but I get force close for second activity.
Can anyone help me how to use it? Thanks...
You can extend Application class and create there an instance of DbAdapter. This way it will be shared by all your activities.
Because db has the same context and life cycle of your first activity. Make your methods public and make them do all the setup/teardown necessary to return your desired result.
regarding the leak warning. Are you closing your db manager connection in onDestroy()?