Right now I am using a static instance of the SQLOpenHelper class like so:
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
private final Context mContext;
//...
public static synchronized DatabaseHelper getInstance(Context context) {
/**
* use the application context as suggested by CommonsWare.
* this will ensure that you don't accidentally leak an Activity's
* context (see this article for more information:
* http://android-developers.blogspot.nl/2009/01/avoiding-memory-leaks.html)
*/
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
}
return mInstance;
}
private DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
mContext = context;
}
//...
}
And then a DatabaseProcessor class like so:
public class DatabaseProcessor {
private SQLiteDatabase mDatabase;
private DatabaseHelper mSQLHelper;
private Context mContext;
public DatabaseProcessor(Context context) {
mContext = context;
mSQLHelper = DatabaseHelper.getInstance(mContext);
}
public void open() throws SQLException {
mDatabase = mSQLHelper.getWritableDatabase();
}
public void close() {
mDatabase.close();
}
//...
}
So if I want to access my database, I do something like this:
DatabaseProcessor mDatabaseProcessor = new DatabaseProcessor(this);
mDatabaseProcessor.open();
mSomeList = mDatabaseProcessor.doSomeQueryAndReturnResults();
mDatabaseProcessor.close();
Is this the correct way to do this? Or is it better to open the database in the base Activity onResume() method and close it during onPause()? How do I correctly throw errors for situations where the database is not open when I try to run a query?
EDIT Refactored version:
public class DatabaseHelper extends SQLiteOpenHelper {
private static SQLiteDatabase mDatabase;
private static DatabaseHelper mInstance = null;
private static Context mContext;
// ...
public static synchronized DatabaseHelper getInstance(Context context) {
/**
* use the application context as suggested by CommonsWare.
* this will ensure that you don't accidentally leak an Activity's
* context (see this article for more information:
* http://android-developers.blogspot.nl/2009/01/avoiding-memory-leaks.html)
*/
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
}
return mInstance;
}
private DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
mContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE_SOME_TABLE); //some SQL expression
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DB_ALTER);
}
public void open() throws SQLException {
mDatabase = getWritableDatabase();
}
public void close() {
mDatabase.close();
}
public boolean isOpen() {
return mDatabase.isOpen();
}
//below this would be various CRUD functions operating on mDatabase
// ...
// ...
}
The best way would be to put your query/transaction statements in try-catch and then release all your resources and close the connection in finally block.
try{
mSomeList = mDatabaseProcessor.doSomeQueryAndReturnResults();
} catch(Exception exc){
//Catch exceptions here
}
finally{
if(mDatabaseProcessor != null)
mDatabaseProcessor.close();
}
Related
Hello as the title says I cannot get the app to construct a small local sqlite database. I would appreciate some help as I m an amateur. Thanks in advance.
Below there are some pictures of my source code.
I tested this code on level 24 API device, but the database does not appear in the data/data/the_package/databases/ folder
Edited to include code
MainActivity.java
public class MainActivity extends AppCompatActivity {
private CrdDBHelper mydb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mydb = new CrdDBHelper(this);
boolean p1 = true;
int p11sc = 0;
int p12sc = 0;
Button btnMenu = (Button) findViewById(R.id.btnMenu);
Button btntrue = (Button) findViewById(R.id.btnTrue);
Button btnFalse = (Button) findViewById(R.id.btnFalse);
// All other available code commented out
}
#Override
protected void onDestroy() {
mydb.close();
super.onDestroy();
}
}
CrdDBContract.java
public final class CrdDBContract {
private CrdDBContract(){}
public static final class GameEntry implements BaseColumns {
public static final String TABLE_NAME = "Game";
public static final String COLUMN_KNOWN = "Known";
public static final String COLUMN_TBF = "TBF";
public static final String SQL_CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + "("
+ _ID + " INTEGER PRIMARY KEY, "
+ COLUMN_KNOWN + " TEXT NOT NULL, "
+ COLUMN_TBF + " TEXT NOT NULL)";
}
}
CrdDBHelper.java
public class CrdDBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "CardGame.db";
public static final int DATABASE_VERSION = 1;
public CrdDBHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CrdDBContract.GameEntry.SQL_CREATE_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
CrdDBDataInsertion.java
public class CrdDBDataInsertion {
//??????????? Code not available from images
SQLiteDatabase mDB;
private void contentvalues(String Known, String TBF) {
ContentValues values = new ContentValues();
values.put(CrdDBContract.GameEntry.COLUMN_KNOWN,Known);
values.put(CrdDBContract.GameEntry.COLUMN_TBF,TBF);
long newid = mDB.insert(CrdDBContract.GameEntry.TABLE_NAME,null,values);
}
}
Main Activity part1Main Activity part2
Main Activity part3(final)
DB Contract class
DB Helper class
DB insert class part1
DB insert class part2 (final)
You aren't, according to the code, acessing(opening) the database. You are just instantiating the DatabseHelper in MainActivity i.e. mydb = new CrdDBHelper(this);
It is not until an attempt is made to open the database (which is frequently implicit) that the database is actually created.
A simple way would be to force an open when instantiating the database. e.g. call the getWritableDatabase() method in the constructor, like :-
public CrdDBHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
getWritableDatabase();
}
Perhaps at that time set an SQLiteDatabase object with the returned SQLiteDatabase, so your Database Helper (CrdDBHelper.java) could be :-
public class CrdDBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "CardGame.db";
public static final int DATABASE_VERSION = 1;
SQLiteDatabase mDB;
public CrdDBHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mDB = this.getWriteableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CrdDBContract.GameEntry.SQL_CREATE_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
as the onCreate method is called when (and ONLY when) the database is created the Game table will then be created.
I would like to ask anyone to please help me to modify my database connection code. What I have now is it's a part of one of my method class and it will make a connection every time I call it ( which I know its very inefficient). But when I try to move the connection part out from the method it breaks the code. I just wonder if I could make a static class somewhere so I can reuse it in other method as well.
Thanks so much for any help in advance, it is really appreciated.
Here is the method code:
public void getListDetail(){
//listDetailData.clear();
ShoppingListDatabase databaseConnection = new ShoppingListDatabase(this);
final SQLiteDatabase db = databaseConnection.open();
final ArrayList<ShoppingItem> lists = ShoppingListItemTable.selectAllItems(db, selectedID);
databaseConnection.close();
//create a list adapter and set adapter
ShoppingListItemAdapter adapter = new ShoppingListItemAdapter(this, R.layout.activity_item_detail_list, lists);
ListView_ListDetail = findViewById(R.id.ListView_ListDetail);
ListView_ListDetail.setAdapter(adapter);
adapter.notifyDataSetChanged();
// ((ArrayAdapter<String>)ListView_ListDetail.getAdapter()).notifyDataSetChanged();
}
Database class:
package com.puyakul.prin.psychic_shopping;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.ContactsContract;
import android.util.Log;
public class ShoppingListDatabase {
private static final String TAG = "ShoppingListDatabase";
private static final String DATABASE_NAME = "ShoppingListDatabase";
private static final int DATABASE_VERSION = 3;
private SQLiteDatabase mDb;
private DatabaseHelper mDbHealper;
private final Context mCtx;
public ShoppingListDatabase(Context ctx){
this.mCtx = ctx;
}
/**
* DatabaseHelper class.
*
* Database helper class to manage connections with the database.
*/
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "DatabaseHelper onCreate");
db.execSQL(ShoppingListItemTable.CREATE_STATEMENT);
db.execSQL(ShoppingListTable.CREATE_STATEMENT);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG, "DatabaseHelper onUpgrade");
db.execSQL("DROP TABLE IF EXISTS " + ShoppingListItemTable.TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + ShoppingListTable.TABLE_NAME);
onCreate(db); //this will recreate the database as if it were new
}
}
public SQLiteDatabase open(){
mDbHealper = new DatabaseHelper(mCtx);
mDb = mDbHealper.getReadableDatabase();
return mDb;
}
public void close(){
mDb = null;
}
}
I have tried to declare variables within onCreate so I can use with other methods in the class like this
//================DATABASE CONNECTION=====================//
private ShoppingListDatabase databaseConnection;
private SQLiteDatabase db = databaseConnection.open();
private String selectedList;
private int selectedID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
setContentView(R.layout.activity_main_lists_detail);
ShoppingListDatabase databaseConnection = new ShoppingListDatabase(this);
SQLiteDatabase db = databaseConnection.open();
lists = ShoppingListItemTable.selectAllItems(db, selectedID);
and this is the error
Process: com.puyakul.prin.psychic_shopping, PID: 5635
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.puyakul.prin.psychic_shopping/com.puyakul.prin.psychic_shopping.MainListsDetail}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase com.puyakul.prin.psychic_shopping.ShoppingListDatabase.open()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2548)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase com.puyakul.prin.psychic_shopping.ShoppingListDatabase.open()' on a null object reference
at com.puyakul.prin.psychic_shopping.MainListsDetail.<init>(MainListsDetail.java:46)
at java.lang.Class.newInstance(Native Method)
at android.app.Instrumentation.newActivity(Instrumentation.java:1078)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2538)
Opening and closing the connection can be inefficient/costly so perhaps introduce/use the following :-
public SQLiteDatabase open(){
if (mDb == null) {
mDbHealper = new DatabaseHelper(mCtx);
mDb = mDbHealper.getReadableDatabase();
}
return mDb;
}
or alternately :-
public synchronized SQLiteDatabase open(){
if (mDb == null) {
mDbHealper = new DatabaseHelper(mCtx);
mDb = mDbHealper.getReadableDatabase();
}
return mDb;
}
and never call the close, except when the main activity is being destroyed. Then you will retrieve the one connection.
Here's some example uses based upon your ShoppingListDatabase class.
First a modified class with a few extra methods inside (addShoppingList, getAllShoppingListsAsCursor and logDBTables) and just a very simple shoppinglist table :-
public class ShoppingListDatabase {
private static final String TAG = "ShoppingListDatabase";
private static final String DATABASE_NAME = "ShoppingListDatabase";
private static final int DATABASE_VERSION = 3;
private static final String TBNAME = "shoppinglist";
public static final String COL_SHOPPINGLIST_NAME = "shoppinglist_name";
private SQLiteDatabase mDb;
private DatabaseHelper mDbHealper;
private final Context mCtx;
public ShoppingListDatabase(Context ctx){
this.mCtx = ctx;
}
/**
* DatabaseHelper class.
*
* Database helper class to manage connections with the database.
*/
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "DatabaseHelper onCreate");
//db.execSQL(ShoppingListItemTable.CREATE_STATEMENT);
//db.execSQL(ShoppingListTable.CREATE_STATEMENT);
String crtsql = "CREATE TABLE If NOT EXISTS " + TBNAME + "(" +
COL_SHOPPINGLIST_NAME + " TEXT " +
")";
db.execSQL(crtsql);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG, "DatabaseHelper onUpgrade");
//db.execSQL("DROP TABLE IF EXISTS " + ShoppingListItemTable.TABLE_NAME);
//db.execSQL("DROP TABLE IF EXISTS " + ShoppingListTable.TABLE_NAME);
onCreate(db); //this will recreate the database as if it were new
}
}
public synchronized SQLiteDatabase open(){
if (mDb == null) {
mDbHealper = new DatabaseHelper(mCtx);
mDb = mDbHealper.getReadableDatabase();
}
return mDb;
}
public void close(){
mDb = null;
}
public long addShoppingList(String name) {
ContentValues cv = new ContentValues();
cv.put(COL_SHOPPINGLIST_NAME,name);
return mDb.insert(TBNAME,null,cv);
}
public Cursor getAllShoppingListAsCursor() {
return mDb.query(TBNAME,
null,
null,
null,
null,null,
null
);
}
public void logDBTables() {
Cursor csr = mDb.query("sqlite_master",null,null,null,null,null,null);
while (csr.moveToNext()) {
Log.d(TAG,"Item " +
csr.getString(csr.getColumnIndex("name")) +
" was created using " +
csr.getString(csr.getColumnIndex("sql"))
);
}
}
}
Here's code from an Activity (that uses the Database connection in various ways) :-
public class MainActivity extends AppCompatActivity {
ShoppingListDatabase mSL;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSL = new ShoppingListDatabase(this);
mSL.open();
SO50312618();
SO50312618_other();
Cursor csr = mSL.getAllShoppingListAsCursor();
while(csr.moveToNext()) {
Log.d("SL INFO","Shopping List " + String.valueOf(csr.getPosition() + 1) +
" is " +
csr.getString(csr.getColumnIndex(ShoppingListDatabase.COL_SHOPPINGLIST_NAME))
);
}
}
private void SO50312618() {
ShoppingListDatabase databaseConnection = new ShoppingListDatabase(this);
SQLiteDatabase db = databaseConnection.open();
databaseConnection.logDBTables();
}
private void SO50312618_other() {
mSL.addShoppingList("List001");
mSL.addShoppingList("List002");
}
}
The results being :-
05-13 05:36:24.290 4245-4245/soanswers.soanswers D/ShoppingListDatabase: Item android_metadata was created using CREATE TABLE android_metadata (locale TEXT)
Item shoppinglist was created using CREATE TABLE shoppinglist(shoppinglist_name TEXT )
05-13 05:36:24.302 4245-4245/soanswers.soanswers D/SLĀ INFO: Shopping List 1 is List001
Shopping List 2 is List002
All the various uses will use the same single connection.
I am really confused how I should be using threads in my Android applications for database interaction. There are too many resources and I don't know which to choose from. So I'm hoping to get more specific and focused advice on my particular situation so I have a starting point.
This is my database class structure, which works great so far:
public class DatabaseHelper extends SQLiteOpenHelper {
private static volatile SQLiteDatabase mDatabase;
private static DatabaseHelper mInstance = null;
private static Context mContext;
private static final String DB_NAME = "database.db";
private static final int DB_VERSION = 1;
private static final DB_CREATE_THINGY_TABLE = "CREATE TABLE blahblahblah...";
//other various fields here, omitted
public static synchronized DatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
try {
mInstance.open();
}
catch (SQLException e) {
e.printStackTrace();
}
}
return mInstance;
}
private DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
mContext = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE_THINGY_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
#Override
public void onConfigure(SQLiteDatabase db){
super.onConfigure(db);
db.setForeignKeyConstraintsEnabled(true);
}
public void open() throws SQLException {
mDatabase = getWritableDatabase();
}
public void close() {
mDatabase.close();
}
public long addNewThingy(String name) {
ContentValues values = new ContentValues();
values.put(DatabaseHelper.THINGY_COLUMN_NAME, name);
return mDatabase.insertWithOnConflict(DatabaseHelper.THINGY_TABLE, null, values, SQLiteDatabase.CONFLICT_IGNORE);
}
public Thingy getThingyById(long id) {
Cursor cursor = mDatabase.query(
DatabaseHelper.THINGY_TABLE, // table
new String[]{DatabaseHelper.THINGY_COLUMN_ID, DatabaseHelper.THINGY_COLUMN_NAME}, // column names
DatabaseHelper.THINGY_COLUMN_ID + " = ?", // where clause
new String[]{id + ""}, // where params
null, // groupby
null, // having
null); // orderby
cursor.moveToFirst();
Thingy thingy = null;
if (!cursor.isAfterLast()) {
String name = getStringFromColumnName(cursor, DatabaseHelper.THINGY_COLUMN_NAME);
thingy = new Thingy(id, name);
cursor.moveToNext();
}
cursor.close();
return thingy;
}
}
So any time I want access to the database I do mDatabaseHelper = DatabaseHelper.getInstance(context); and I am good to go. I don't make any explicit calls to open() or close() or anything like that. However right now I am making all my database calls on the UI thread, I believe (either in my onCreate or onCreateView methods or separate methods which don't invoke any new threads or anything).
How would I correctly make this threaded so that I am not performing database operations on the UI thread?
I figure I have to change all my database calls to basically do this:
Make any necessary edits to my database class first to ensure it will work properly in the event that multiple threads are trying to perform operations at the same time. I already tried by making my class a singleton (I think it's a singleton, anyway?) and using keywords like "volatile" and "synchronized" but maybe I am missing something somewhere.
Perform database operation in its own thread.
Somehow trigger additional code back in the appropriate function/activity/fragment that will execute once the database operation has completed.
Make this whole process versatile enough to where I can do it anywhere.
Am I making sense? Is this the right way to be going about all this? Is there a simple example you can make that can show me how to, for example, correctly do something like mThingy = mDatabaseHelper.getThingyById(id); or mDatabaseHelper.addNewThingy(someName); from a sample activity/fragment/etc using proper threading?
Simple solution using Threads
public class DatabaseHelper extends SQLiteOpenHelper {
//...
public void addNewThingyAsync(final String name, final Callback<Long> cb) {
new Thread(new Runnable(){
#Override
public void run(){
cb.callback(addNewThingy(name));
}
}).start();
}
private synchronized long addNewThingy(String name){
//implementation...
}
public void getThingyByIdAsync(final long id, final Callback<Thingy> cb) {
new Thread(new Runnable(){
#Override
public void run(){
cb.callback(getThingyById(id));
}
}).start();
}
private synchronized Thingy getThingyById(long id) {
//implementation...
}
public interface Callback<T> {
public void callback(T t);
}
}
Solution using AsyncTasks
Same as above with the following changes:
public class DatabaseHelper extends SQLiteOpenHelper {
//...
public void addNewThingyAsync(final String name, final Callback<Long> cb) {
new AsyncTask<Void, Void, Long>(){
#Override
protected Long doInBackground(Void... ignoredParams) {
return addNewThingy(name);
}
#Override
protected void onPostExecute(Long result) {
cb.callback(result);
}
}.execute();
}
//...
public void getThingyByIdAsync(final long id, final Callback<Thingy> cb) {
new AsyncTask<Void, Void, Thingy>(){
#Override
protected Thingy doInBackground(Void... ignoredParams) {
return getThingyById(id);
}
#Override
protected void onPostExecute(Thingy result) {
cb.callback(result);
}
}.execute();
}
//...
}
Calling (works with both approaches)
long mId = ...;
mDatabaseHelper = DatabaseHelper.getInstance(context);
mDatabaseHelper.getThingyByIdAsync(mId, new Callback<Thingy>{
#Override
public void callback(Thingy thingy){
//do whatever you want to do with thingy
}
});
How would I correctly make this threaded so that I am not performing
database operations on the UI thread?
Simply perform any database operations off the UI thread. A common technique involves an AsyncTask. For example:
public class GetThingyTask extends AsyncTask<Long, Void, Thingy> {
private Context context;
public AddTask(Context context){
this.context = context;
}
#Override
protected Thingy doInBackground(Long... ids) {
final long id = ids[0];
Cursor cursor = DatabaseHelper.getInstance(context).query(
DatabaseHelper.THINGY_TABLE,
new String[]{
DatabaseHelper.THINGY_COLUMN_ID,
DatabaseHelper.THINGY_COLUMN_NAME
},
DatabaseHelper.THINGY_COLUMN_ID + "=?",
new String[]{String.valueOf(id)},
null, null, null);
String name = null;
if (cursor.moveToFirst() && (cursor.getCount() > 0)) {
name = getStringFromColumnName(cursor, DatabaseHelper.THINGY_COLUMN_NAME);
}
cursor.close();
return new Thingy(id, name);
}
#Override
protected void onPostExecute(Thingy thingy) {
//Broadcast the Thingy somehow. EventBus is a good choice.
}
}
And to use it (inside, for example, an Activity):
new GetThingyTask(this).execute(id);
I am a little confused how to do this properly. Here is my DatabaseHelper class:
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
private static final String DB_NAME = "database.db";
private static final int DB_VERSION = 1;
private Context mContext;
public static DatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
}
return mInstance;
}
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
mContext = context;
}
...
Assuming this is right, is this the correct way to handle the querying class:
public class DatabaseProcessor {
private SQLiteDatabase mDatabase;
private DatabaseHelper mSQLHelper;
private Context mContext;
public DatabaseProcessor(Context context) {
mContext = context;
mSQLHelper = new DatabaseHelper(mContext);
}
private void open() throws SQLException {
mDatabase = mSQLHelper.getWritableDatabase();
}
private void close() {
mDatabase.close();
}
public void insertSomethingIntoDb(String key) {
ContentValues values = new ContentValues();
values.put("some_column_name", name);
open();
mDatabase.insert("some_table_name", null, values);
close();
}
...
And if this is right, how do I properly invoke a db method from somewhere else in the code such as an Activity, Fragment, etc, anywhere.
You should make the DatabaseHelper constructor private and create instances of this class by using the getInstance method. eg: mSQLHelper = DatamabseHelper.getInstance(context).
To call a method of this class, you can do something like this.
DatabaseHelper.getInstance(context).someFunction(...);
And to use any of the DatabaseProcessor functions, you can do this:
new DatabaseProcessor(context).insertSomethingIntoDb(...);
Keep in mind that this singleton approach has some problems, for starters, it doesn't support multithreading, there is no mechanism in place to assure that if two threads ask for an instance at the same time, only one instance will be created.
I have no idea how to do proper concurrent operations, so I just tried to adjust my code.
And totally lost in concurrency in constructor and concurrency with static, final fields...
public static class Connection {
private final CustomerDatabaseOpenHelper mHelper;
private SQLiteDatabase mDatabase;
private String mUserId;
private AtomicInteger mOpenCounter;
public Connection(Context context, String userId) {
mHelper = CustomerDatabaseOpenHelper.getInstance(context, DB_NAME, null, DB_VERSION);
mOpenCounter = mHelper.mOpenCounter;
mUserId = userId;
}
public void open() throws SQLException {
// open database in reading/writing mode
int value = mOpenCounter.incrementAndGet();
if(value == 1 || mDatabase==null || mUserId ==null) {
// Opening new database
Log.v("tugce","open new customer db");
mDatabase = mHelper.getWritableDatabase();
}
};
public void close() {
int value = mOpenCounter.decrementAndGet();
if(value == 0) {
// Closing database
if (mDatabase != null) {
try {
Log.v("tugce","close customer db");
mDatabase.close();
mDatabase = null;
mUserId = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//my openhelper class
public class CustomerDatabaseOpenHelper extends SQLiteOpenHelper{
public AtomicInteger mOpenCounter = new AtomicInteger();
public static CustomerDatabaseOpenHelper mInstance;
public static synchronized CustomerDatabaseOpenHelper getInstance(Context context, String name,
CursorFactory factory, int version) {
if (mInstance == null) {
mInstance = new CustomerDatabaseOpenHelper(context, name, factory, version);
}
return mInstance;
}
private CustomerDatabaseOpenHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
This is how I am using in threads:
class MyGetPlanedCallList extends
AsyncTask<Object, Object, List<MyPlannedCallListItem>> {
CustomerDatabase.Connection mDbCon = new CustomerDatabase.Connection(InstanceActivity.this, mUserId);
#Override
protected void onPreExecute() {
mDbCon.open();
}
#Override
protected List<MyPlannedCallListItem> doInBackground(Object... params) {
mDbCon.doSomeStuff();
}
#Override
protected void onPostExecute(List<MyPlannedCallListItem> result) {
mDbCon.close();
}
#Override
protected void onCancelled() {
if (mDbCon != null)
mDbCon.close();
}
}
What I want is, exactly same db shouldn't be closed if some other instance is working on it.
Any help would be appreciated.