Access values of SQLite loader in thread - android

I'm using SQLIte loader (cwac-loaderex). i'm performing set of operation in a background thread. the operation includes inserting new record, updating the existing one as well as deleting the record.
These operations i'm performing on cursor object like this. these methods are present in loader class & i'm accessing these from my background thread.
#Override
public void Insert(String table, String nullColumnHack,
ContentValues values) {
mLoader.insert(table, nullColumnHack, values);
}
#Override
public void Update(String table, ContentValues values,
String whereclause, String[] whereargs) {
// TODO Auto-generated method stub
mLoader.update(table, values, whereclause, whereargs);
}
In few scenarios like. Once i insert record, i need the primary key to update another record. i'm failing to get those. Once thread completes its execution the UI will get updated and i'm able to access the required value onPostexecute method. But i want the values soon after i insert to Database.
As per i know, that to reflect the updated values i'm calling the insert, update & delete method directly on Loader object.
How to access the inserted row, in the same thread soon after insert is called?

Related

App crashes between onPause and onResume Listview issue

I have a listview activity which populates data through an sqlite database; however, whenever I enter onPause and then go into onResume my app crashes and I receive this error: "java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor#418106a8". Would anyone know how to stop this? Is there a method I have to call in onPause?
#Override
protected void onResume() {
super.onResume();
uGraduateListAdapter = new ArrayAdapter<String>(ListOfAlarms.this, android.R.layout.simple_list_item_1, populateList());
listOfAlarms.setAdapter(uGraduateListAdapter);
Log.i(TAG, "Resume was called");
}
#Override
protected void onPause() {
super.onPause();
Log.i(TAG, "Pause was called");
sqliteDatabase.close();
}
public List<String> populateList(){
// We have to return a List which contains only String values. Lets create a List first
List<String> uGraduateNamesList = new ArrayList<String>();
// First we need to make contact with the database we have created using the DbHelper class
AndroidOpenDbHelper openHelperClass = new AndroidOpenDbHelper(this);
// Then we need to get a readable database
sqliteDatabase = openHelperClass.getReadableDatabase();
// We need a a guy to read the database query. Cursor interface will do it for us
//(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
cursor = sqliteDatabase.query(AndroidOpenDbHelper.TABLE_NAME_ALARM, null, null, null, null, null, null);
// Above given query, read all the columns and fields of the table
startManagingCursor(cursor);
// Cursor object read all the fields. So we make sure to check it will not miss any by looping through a while loop
while (cursor.moveToNext()) {
// In one loop, cursor read one undergraduate all details
// Assume, we also need to see all the details of each and every undergraduate
// What we have to do is in each loop, read all the values, pass them to the POJO class
//and create a ArrayList of undergraduates
String alarmName = cursor.getString(cursor.getColumnIndex(AndroidOpenDbHelper.COLUMN_NAME_ALARM_NAME));
// String ugUniId = cursor.getString(cursor.getColumnIndex(AndroidOpenDbHelper.COLUMN_NAME_UNDERGRADUATE_UNI_ID));
String alarmTotalTime = cursor.getString(cursor.getColumnIndex(AndroidOpenDbHelper.COLLUMN_ALARM_TOTALTIME));
// Finish reading one raw, now we have to pass them to the POJO
TestAlarm ugPojoClass = new TestAlarm();
ugPojoClass.setTitle(alarmName);
ugPojoClass.setTotalTime(alarmTotalTime);
// Lets pass that POJO to our ArrayList which contains undergraduates as type
pojoArrayList.add(ugPojoClass);
// But we need a List of String to display in the ListView also.
//That is why we create "uGraduateNamesList"
uGraduateNamesList.add(alarmName);
}
// If you don't close the database, you will get an error
sqliteDatabase.close();
return uGraduateNamesList;
}
You are using deprecated methods (startManagingCursor()), which is dangerous.
How I see what happens: when you close your database (twice actually: in populateList() and onPause()), your cursors to this database become invalid. But since you called startManagingCursor(), your Activity retains your cursors and tries to call requery() on them when restarting, which throws the error.
Try not calling startManagingCursor() at all, just cursor.close() when you're done with it. Or you can migrate to newer LoaderManager altogether.

query() is called again after notifyChange()

I am using a custom content provider and a CursorLoader for displaying a list of elements in a fragment.
First the CursorLoader calls the ContentProvider's query() to get all elements stored in my Database. In my query() function I start a thread which does a WebService call to update the elements in my database. The thread is parsing the WebServices' response and calling my ContentProviders bulkInsert().
When my bulkInsert() is done, i call notifyChange().
Now here is whats happening: I see that after the notifyChange(), my ContentProvider's query() method is called again, which leads to a new WebService call and so on and so on.
Here my query() method in the ContentProvider:
...
// Database query
ticketCursor = mDb.query(TBL_TICKET_NAME, projection, selection, selectionArgs, null, null, orderBy);
// set Notification URI
ticketCursor.setNotificationUri(getContext().getContentResolver(), TICKET_CONTENT_URI);
// WebService call --> This starts a new Thread for the WebService call
asyncSoapQuery(where, uri);
return ticketCursor;
My bulkInsert method looks like this:
mDb.beginTransaction();
for (ContentValues value : values) {
mDb.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_REPLACE);
}
mDb.setTransactionSuccessful();
getContext().getContentResolver().notifyChange(TICKET_CONTENT_URI, null);
mDb.endTransaction();
return values.length();
So, my problem is that this causes an endless loop of webservice calls. I thought, that the notifyChange() is not calling the content provider's query() method again. I only want to do the WS call inside my query() again if the user hits "refresh" on my UI...
What am I missing here?
Thanks in advance for your help!
I have the same issue but I'm using insert() instead of applyBatch() in the content provider. I manage to resolve my issue by changing
mDb.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_REPLACE);
to
long rowId = mDb.insertWithOnConflict(table, null, value, SQLiteDatabase.CONFLICT_IGNORE);
My next step is to check if rowId is greater than zero. Only when it is greater than zero then execute
getContext().getContentResolver().notifyChange
So it is something like this:
if (rowId > 0) {
notifyChange(uri);
return uri;
} else {
return null;
}

Android SQLite batch insertion

I need to insert a lot of rows in the database at a time and I'm seeking for the most efficient way to do it. I have seen code like this:
db.beginTransaction();
for (ModelObject object : modelObjectsCollection){
ContentValues values = new ContentValues();
... // fill values variable with values from object
db.insert(TABLE_NAME, null, values);
values.clear();
}
db.setTransactionSuccessful();
In this case all inserts are accomplished as a single operation which takes less time. Will it still work as a single operation if I incapsulate insertion of a single row into a method like this:
public void insertAllRows(){
db.beginTransaction();
for (ModelObject object : modelObjectsCollection){
insertSingleRow(object);
}
db.setTransactionSuccessful();
}
public void insertSingleRow(ModelObject object){
ContentValues values = new ContentValues();
... // fill values variable with values from object
db.insert(TABLE_NAME, null, values);
}
Will it be accomplished in a single transaction as well?
Besides, I do not understand: is it correct that if we do not call
db.beginTransaction(); ... db.setTransactionSuccessful();
explicitly, but only call db.insert(), beginTransaction()-setTransactionSuccessful() methods are invoked inside insert(). In contrary, if we invoke insert() between invokations of beginTransaction()-setTransactionSuccessful(), the latter 2 methods aren't invoked inside insert()?
You can execute queries without transactions at all. And there are no need in transaction for single query at all.
Also you need to call db.endTransaction(); after db.setTransactionSuccessful() for commiting it.
upd: you can find more about common transaction use here http://www.4js.com/online_documentation/fjs-fgl-manual-html/User/Transactions.html for example.

Closing the SQLiteDatabase in a Content Provider

I'm looking through the NotePad example that comes with the Android SDK and I was wondering if someone could clarify why the the variable db is never closed in the update function? It's usually a good idea to close the database when it is no longer in use to prevent leaks. Ideas?
#Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
// Opens the database object in "write" mode.
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
... // More code, db.close() is never called?!
// Returns the number of rows updated.
return count;
}

Android Contentprovider - update within an insert method

Is it ok to call the SQLiteDatabase update method in the insert() overridden method of a content provider?
Basically it's fine, but since you didn't provided code, I just can post 2 possible ways for it
First:
// In your content provider
public Uri insert(...) {
long insertId = db.insert(...);
if(insertId == -1) {
// insert failed, do update
db.update(...);
}
}
Second:
public Uri insert(...) {
long insertId = db.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE)
if(insertId == -1) {
// insert and update/replace failed
}
}
Check out SQLiteDatabase for reference on the forth parameter. In most cases the last method should be sufficient, unless you want only certain fields being updated if the row exists and have all fields if it doesn't.
Most useful need for insertWithOnConflict may be, that you could insert a row and if it already exists, ignore the insert and instead return the Uri/primary key of the already existing row.
It's your choice what you write in your overridden methods.
So yes, it is ok.
I don't know what you're trying to do, but you might want to to take a look on the SQLiteDatabase's replace() method too. Maybe it better suits your needs.

Categories

Resources