I have implemented the class found in this question:
CursorLoader usage without ContentProvider
It is a means of using the LoaderManager and CursorLoader without a content resolver. I am using it to load data from a SQLite database and display it in a ListFragment.
The problem I am seeing is that the database is leaking. Obviously this is because I am not closing the database when I am done.
I have now started to do this, but I am concerned as the database could be accessed at any time by background tasks scheduled with the AlarmManager. I am worried that I might close the database when another class needs it open.
My solution has been to count the opens/closes and only close the database when no one is using it. Like so:
public synchronized SQLiteDatabase openDataBase()
{
try
{
mDatabaseUsers++;
Log.d(TAG, "DatabaseUsers: " + mDatabaseUsers);
// If already open, return it.
if (mOpenDatabase != null && mOpenDatabase.isOpen())
return mOpenDatabase;
OpenHelper openHelper = new OpenHelper(mContext);
return openHelper.getWritableDatabase();
} catch (SQLException e)
{
Log.e("MessageDelay", "Error opening database: " + e.toString());
return null;
}
}
public synchronized void closeDatabase()
{
mDatabaseUsers--;
// If no one is using the database, close it.
if (mOpenDatabase != null && mDatabaseUsers == 0)
{
mOpenDatabase.close();
}
Log.d(TAG, "DatabaseUsers: " + mDatabaseUsers);
}
This appears to work, but it has meant adding an extra line of code all over my application. Furthermore I've had trouble with the LoaderManager not behaving as expected and it calls its reset function more than it does its load, so I've had to put this fix in:
return new SimpleCursorLoader(getActivity())
{
private int mDBOpens = 0;
#Override
public Cursor loadInBackground()
{
mDBOpens++;
return JSQLite.getSingleton(getActivity()).retrieveTextsSent(mMode == 1 ? true : false);
}
#Override
public void reset()
{
if (mDBOpens > 0)
{
JSQLite.getSingleton(getContext()).closeDatabase();
}
super.reset();
mDBOpens--;
}
};
It feels like this isn't the correct way of doing it. Is there another, cleaner means of closing/opening the database only when needed?
Thanks, Jason.
Related
I am querying POJO which is NOT being Observed / Non-Live data from an IntentService that was started in a PreferenceFragment. However a second my application crashes and log displays:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:204)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:232)
at vault.dao.xxxDao_Impl.getAllNonLivePojoItems(xxxDao_Impl.java:231)
I want to know why is my program throwing this exception. as per https://stackoverflow.com/a/23935791/8623507
my database query[s] are inside an IntentService that runs In its own thread so i should be in the green. here is my code:
Inside IntentService
--------------------
// ERROR OCCURS HERE
List<POJO> pojoList = localRepo.getAllNonLivePojoItems(); // <= ERROR POINTS HERE
if (pojoList != null && pojoList.size() > 0) {
for (Pojo pojo : pojoList ){
// Do Long Running Task Here ....
}
Also I instantiate The Objects Being Used and call the above methods from those Objects Throughout the IntentService in OnHandleIntent like so:
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
LocalRepo localRepo = new LocalRepo(this.getApplication());
PojoHelper pojoHelper = new PojoHelper(this, localRepo);
if (LOGOUT.equals(action) && type != null) {
Log.d(TAG, "onHandleIntent: LOGOUT");
pojoHelper.logoutPojo();
}
else if(DELETE.equals(action) && type != null){
Log.d(TAG, "onHandleIntent: DELETE_POJO");
pojoHelper.deletePojo(true);
}
}
}
I assume you get callback from AsyncTask onPostExecute() method which runs on UI thread. It is prohibited to use database or network calls inside UI thread because it can block UI.
Execute your code where you access database inside new thread.
Example:
Executors.newSingleThreadExecutor().execute(()->{
//TODO access Database
});
One thing i failed to mention was that the method was being executed within an async's response callback method
PojoWarehouse.processPojoItems(new AsyncPojoCallback() {
#Override
public void done(Exception e) {
if (e == null) {
// ERROR OCCURS HERE
List<POJO> pojoList = localRepo.getAllNonLivePojoItems(); // <= ERROR POINTS HERE
if (pojoList != null && pojoList.size() > 0) {
for (Pojo pojo : pojoList ){
// Do Long Running Task Here ....
}
} else
Log.d(TAG, "done: Error Logging Out: " + e.getLocalizedMessage());
}
});
I cannot explain on a technical level why this fixed the issue, however suggestions are welcomed.
i'm having an issue that soon enough going to blow me.
i have Database table lets call it A. table A has field that determines if this row is processed or no. i update the field myself from within the Parse Browser to either True | False, and trying to call query.findInBackground() to check with the Boolean value however the returned List always returns False if its True and vice versa. enough talking let me show you what i'm doing.
public static void getMyRequests(ParseUser user, final FindCallback<ServicesModel> callback) {
ParseQuery<ServicesModel> query = new ParseQuery<>(ServicesModel.class);
if (!user.getBoolean(ParseHelper.CAN_UPLOAD)) {
query.whereEqualTo("user", user);
}
query.findInBackground(new FindCallback<ServicesModel>() {
#Override public void done(final List<ServicesModel> objects, ParseException e) {
if (e == null) {
if (objects != null && !objects.isEmpty()) {
for (ServicesModel object : objects) {
object.setHandlerUser(object.getParseUser("handlerUser"));
object.setProcessedTime(object.getLong("processedTime"));
object.setCategoryType(object.getString("categoryType"));
object.setUser(object.getParseUser("user"));
object.setUserRequest(object.getString("userRequest"));
object.setImageUrl(object.getString("imageUrl"));
object.setProcessed(object.getBoolean("isProcessed"));
Logger.e(object.getBoolean("isProcessed") + "");
}
callback.done(objects, null);
} else {
callback.done(null, new ParseException(1001, "No Services"));
}
} else {
callback.done(null, e);
}
}
});
}
the code above suppose to refresh my data but however my log always shows that isProcessed is False even tho it's set to True inside the Parse Browser
what i have tried besides this? fetchAllInBackground & fetch() you name it. the object will always return false until i re-run the application from Android Studio what i'm doing here wrong? btw here is how i initialize Parse
Parse.setLogLevel(BuildConfig.DEBUG ? DEBUG_LEVEL : Parse.LOG_LEVEL_NONE);
ParseObject.registerSubclass(ProductsModel.class);
ParseObject.registerSubclass(ProductRentalModel.class);
ParseObject.registerSubclass(ServicesModel.class);
Parse.enableLocalDatastore(context);
Parse.initialize(context, context.getString(R.string.app_id), context.getString(R.string.client_id));
the answer was to remove
Parse.enableLocalDatastore(context);
which is bad anyway, without the datastore enabled the data are refreshed probably, however with enabling the local database, the data will not refresh unless if i killed the app and/or re-install it. that's bad. but did the trick.
One of the methods of my app should set the data into its database.
The problem I'm facing is that the insert method (Provided by SQLite Android Class) does not insert any data at the end and it didn't throws an exception either.
Here is the method's code:
public void setGasto(int idRubrica, String estabelecimento, float valor){
SQLiteDatabase db2 = this.getWritableDatabase();
ContentValues values = new ContentValues(4);
values.put("DATA", System.currentTimeMillis());
values.put("VALOR",valor);
values.put("ESTABELECIMENTO", estabelecimento);
values.put("ID_RUBRICA",idRubrica);
db2.beginTransaction();
try{
db2.insertOrThrow("GASTOS",null,values);
} catch (Exception e){
System.out.println(e);
}
db2.endTransaction();
db2.close();
}
I got stuck with it.
Update.
I think that the problem resides in the way I'm calling it.
I call it from a Inner Method that is set to an Alert Dialog Builder.
public void dismiss(AlertDialog.Builder builder, final SQLiteDatabase db2){
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
db.setGasto(db2, idRubrica,estabelecimento,valor);
}
});
}
You need to call setTransactionSuccessful() before endTransaction() to commit the changes. Otherwise endTransaction() rolls back the changes.
The canonical pattern for transactions is as follows:
beginTransaction();
try {
db operations...
setTransactionSuccessful();
}
finally {
endTransaction();
}
This ensures the begin and end calls are always paired.
I have an ImageButton that I want to use to set a boolean value for an item in an SQLiteDatabase.
The ImageButton will display one image for a value of "1", and another image for a value of "0". Pressing the image button should toggle the database field and therefore its image.
For some reason, when I press the button the value returned by the currentCursor.getInt() is still the same, despite the update method being called on the database. Do I have to update/refresh the cursor?
In my activity I have:
private void updateFavouriteButton(){
int favourite = currentCursor.getInt(currentCursor.getColumnIndex(Object.favourite));
if (favourite == 1)
{
favouriteButton.setImageDrawable(getResources().getDrawable(R.drawable.favourite_selected));
favouriteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
dbHelper.updateFavourite(selectedId, 0);
updateFavouriteButton();
}
});
}
else
{
favouriteButton.setImageDrawable(getResources().getDrawable(R.drawable.favourite));
favouriteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
dbHelper.updateFavourite(selectedId, 1);
updateFavouriteButton();
}
});
}
}
In my database helper I have:
public void updateFavourite(long rowId, int favourite)
{
db.beginTransaction();
try {
ContentValues args = new ContentValues();
args.put("ZFAVOURITE", favourite);
int rowsAffected = db.update("ZOBJECT", args,"_id" + "=" + rowId, null);
if (rowsAffected > 0)
db.setTransactionSuccessful();
} catch (Exception e) {
Log.e("Error in transaction", e.toString());
} finally {
db.endTransaction();
}
}
requery() is deprecated, the documentation says to just close the old cursor, remake it, and use the new one.
A cursor is a snapshot at the time its created. If the Database is changed, you must requery.
As #Pyrodante had said, it is needed to requery() the cursor in some ways since requery() is deprecated
Not sure how u guys solve it, here is how I solve this problem.
please let me know if there is a better way. thanks!
Cursor newCursor=CreateCursor(); //ChreateCursor() create a method which returns you a new cursor
mAdapter.changeCursor(newCursor); //Change to the new cursor so that the list will be updated
I have an ExpandableListView that uses a SimpleCursorTreeAdapter which uses the cursors returned by a ContentProvider. This is fine as it always keeps in sync with the data but sometimes I need to do many changes to the database so that the cursor is requeried many times in the same second. Is it possible to suspend the notification of ContentObservers to avoid unnecessary requerys?
A possible solution is to modify the content provider to allow suspending notifications. URIs to be notified are added to a queue until suspension is disabled.
private boolean suspendNotifications = false;
private LinkedList<Uri> suspendedNotifications = new LinkedList<Uri>();
private HashSet<Uri> suspendedNotificationsSet = new HashSet<Uri>();
private void notifyChange(Uri uri) {
if (suspendNotifications) {
synchronized (suspendedNotificationsSet) { // Must be thread-safe
if (suspendedNotificationsSet.contains(uri)) {
// In case the URI is in the queue already, move it to the end.
// This could lead to side effects because the order is changed
// but we also reduce the number of outstanding notifications.
suspendedNotifications.remove(uri);
}
suspendedNotifications.add(uri);
suspendedNotificationsSet.add(uri);
}
}
else {
getContext().getContentResolver().notifyChange(uri, null);
}
}
private void notifyOutstandingChanges() {
Uri uri;
while ((uri = suspendedNotifications.poll()) != null) {
getContext().getContentResolver().notifyChange(uri, null);
suspendedNotificationsSet.remove(uri);
}
}
private void setNotificationsSuspended(boolean suspended) {
this.suspendNotifications = suspended;
if (!suspended) notifyOutstandingChanges();
}
#Override
public Uri insert(Uri uri, ContentValues values) {
...
notifyChange(uri);
return newItemUri;
}
I'm not sure how to best enable/disable suspension but one possibility would be to have a special URI which turns on/off suspension (e.g. content://<authority>/suspension) in the update() method:
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
...
case SUSPEND:
boolean enabled = values.getAsBoolean("enabled");
setNotificationsSuspended(enabled);
break;
...
}
}
The service that does the changes to the database can now suspend the ContentProvider when it starts and disable suspension when it finishes.
Can you use http://developer.android.com/reference/android/widget/BaseAdapter.html#unregisterDataSetObserver(android.database.DataSetObserver) to unRegister your listeners and once your work is ready, register them again? Sounds like a AsyncTask to me.