Android Realm: No results when added in singleton - java

I'm testing out Realm for database storage.
I'm using a singleton for fetching and managing common data that needs to be refreshed fairly often. But it seems that the Realm defaultInstance that get in my singleton is not in the same scope as if I get it in my Activity. So when I fetch remote data via my singleton, then save to realm, I can't retrieve that data from an Activity (get an empty result set).
I have attempted to pass in the Realm instance I defined in the Activity to the singleton (and close it there as well), but I still cannot retrieve saved results via the Activity instance.
I'm setting the default configuration in my Application class if that makes a difference.
Any help would be appreciated in clearing this up.
**Edit
Here's some more code. I'm using retrofit and gson, and my relevant services are in a Utility class (which may be causing the issue).
private void fetchMyObjects(Context context) {
// Fetch the myObjects
UtilityServices utilityServices = new UtilityServices(context);
utilityServices.getMyObjects(new UtilityServices.MyObjectsListener() {
#Override
public void gotMyObjects(final ArrayList<MyObject> myObjects, Exception e) {
if(e == null) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.delete(MyObject.class);
realm.copyToRealm(myObjects);
Log.v("qwer", "LocalDataFragment fetchMyObjects: " + realm.where(MyObject.class).findAll().size());
}
});
} finally {
if(realm != null) {
realm.close();
}
}
} else {
// TODO: Handle a myObject error.
e.printStackTrace();
}
}
});
}

There is only one way that the results of a Realm transaction would not be visible after a transaction is executed and that is that the transaction takes place on a different thread.
It seems quite likely that this is the case, in your code, since, if getMyObjects ran on the UI thread, you would be getting the "no network activity on the UI thread exception"

Related

How to use a single Firestore instance throughout an android app?

I'm trying to use a single Firestore instance throughout an app because with Firebase's cold start the first query or the first data to be fetched takes sometime(like 2000ms-3000ms). I tried having Firestore object as static in a class that extends Application but it shows a Lint warning "Do not place Android context classes in static fields; this is a memory leak". I don't know where to go from here. Please help me.
getInstance() will get you an unique instance throught your whole app, when you use
FirebaseFirestore.getInstance()
or
FirebaseDatabase.getInstance()
It will not create another instance if you are creating them in different classes, instead, it will reuse the same one over and over again.
You can also see the implementation by ctrl + click on the method in your IDE
#NonNull
public static FirebaseFirestore getInstance() {
FirebaseApp app = FirebaseApp.getInstance();
if (app == null) {
throw new IllegalStateException("You must call FirebaseApp.initializeApp first.");
} else {
return getInstance(app, "(default)");
}
}
#NonNull
public static FirebaseApp getInstance() {
synchronized(LOCK) {
FirebaseApp defaultApp = (FirebaseApp)INSTANCES.get("[DEFAULT]");
if (defaultApp == null) {
throw new IllegalStateException("Default FirebaseApp is not initialized in this process " + ProcessUtils.getMyProcessName() + ". Make sure to call FirebaseApp.initializeApp(Context) first.");
} else {
return defaultApp;
}
}
}

Firestore NullPointerException on retrieve firebase.Timestamp.toDate (FieldValue.serverTimestamp update slow to update)

After calling a batch.commit or docRef.update (part of it calls FieldValue.serverTimeStamp to update time of submission), I call finish(); to go back to previous activity that loads a recycleView of the list of documents that was updated.
I get this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.Date com.google.firebase.Timestamp.toDate()' on a null object reference`
I suspect it's that FieldValue.servertimeStamp takes more time to compute and the app crashes. However, the same field where recyclerView is pulling the datetime from already have an old value.
I'm not sure why the old value is not retrieved, but crashes on null instead.
Q1) Does FieldValue.servertimeStamp make the field null until new datetime is computed?
My guess is, this particular call is waiting for an answer from Firebase server, thus taking more time but other calls are done locally first on the device before updating in the cloud. Some of your insights is appreciated.
In the mean time, as a work-around to stop this asynchronous error, I have used a Thread loop with Thread.sleep while waiting for onCompleteSuccess to respond:
FirestoreFieldUpdated = false;
Thread myThread = new Thread(
new Runnable() {
#Override
public void run() {
try {
while (!FirestoreFieldUpdated) { //db.updateFields will change FirestorefieldUpdated to true uponSuccess
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
finish();
}
}
}
);
myThread.start();
Q2) Is there a more elegant way or better way to do this? Or to enable synchronicity only for this particular transaction of updating datetime?
EDIT (added on more details on what I'm trying to do):
I am trying to call this method from AddNewOrder.java:
public void updateFields(String actionDateField) {
Map<String, Object> updates = new HashMap<>();
updates.put(actionDateField, FieldValue.serverTimestamp());
updateSubmit.update(updates);
}
from a class outside (AddNewOrder.java):
db = new DatabaseHelper(getApplicationContext());
db.updateFields("OrderDate");
finish();
Finish(); will then pass me back to the previous activity that calls RecyclerView:
Query query = mFirestore
.collection("Org")
.document(RootCollection)
.collection("Stores")
.document(StoreID)
.collection("Orders")
.whereGreaterThan("OrderVerified", "")
.limit(queryLimit);
mAdapter = new OrdersAdapter(query, FulfilmentActivity.this) {
#Override
protected void onError(FirebaseFirestoreException e) {
// Show a snackbar on errors
Snackbar.make(findViewById(android.R.id.content),
"Error: check logs for info.", Snackbar.LENGTH_LONG).show();
}
};
mAdapter.setQuery(query);
mOrdersRecycler.setAdapter(mAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mOrdersRecycler.setLayoutManager(layoutManager);
In OrdersAdapter.java, I have this:
Orders orders = snapshot.toObject(Orders.class);
submitDate.setText(FORMAT.format(orders.getOrderDate()));
in public void bind.
The above is the line that NullPointerException appeared on.
Orders.java:
public class Orders {
private Timestamp OrderDate;
public Orders(Timestamp orderDate) { this.OrderDate = orderDate; }
public java.util.Date getOrderDate() { return OrderDate.toDate(); }
}
How do I fix this properly?
First of all, if you're working with threading in order to deal with Firestore, you're almost certainly doing the wrong thing. All Firestore APIs (actually, all Firebase APIs) are asynchronous, and require no threading on the part of your app.
Q1 - there is no intermediate null value in a document that's going to be created with a timestamp. The server interprets the server timestamp token immediately and writes a Timestamp object to the document atomically.
Q2 - I can't really tell what you're trying to accomplish with this code. It's way out of bounds of what you would normally do to write to Firestore. If you want to know if a document has changed in Firestore, you attach a listener to a reference to that document, and the listener is invoked when the document is seen to change. Again, there's no need for threading in this case, because the callbacks are all asynchronous. As long as the listener is added, it will be called.
serverTimeStamp() indeed becomes Null in the cache before getting a confirm response from the server: https://github.com/firebase/firebase-js-sdk/issues/192
Refer to this for solution: https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/Document
I fixed this by adding this:
DocumentSnapshot.ServerTimestampBehavior behavior = ESTIMATE;
Date date = snapshot.getDate("OrderDate", behavior);

Realm objects can only be accessed on the thread they where created? [duplicate]

So inside a IntentService, the app maybe active or inactive , onHandleIntent gets called , where I have placed this below code.This is where I store the data into realm.
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for (AppItem item : items) {
AppItem item2 = realm.createObject(AppItem.class, UUID.randomUUID().toString());
item2.mName = item.mName;
item2.mCount = item.mCount;
item2.mUsageTime = item.mUsageTime;
}
}
});
} finally {
if (realm != null) {
realm.close();
}
}
Then I am trying to access it in onPostExecute in AsyncTask, In doInBackground, I am getting the RealmResults<AppItem>, then storing it into List <AppItem> and sending it to onPostExecute where this code is placed. appItems is ReamObject here
Realm backgroundRealm = Realm.getDefaultInstance();
backgroundRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for (AppItem item : appItems) {
//getting error here if (item.mUsageTime <= 0) continue;
mTotal += item.mUsageTime;
item.mCanOpen = mPackageManager.getLaunchIntentForPackage(item.mPackageName) != null;
}
}
});
Both I have done using executeTransactionAsync, still I get the following error.
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
As per the error message: Once you obtain any Realm objects on 1 thread, they can ONLY be accessed on that thread. Any access on other threads will throw that exception.
"doInBackground" from AsyncTask is run on a background thread. "onPostExecute" is run on the UI thread. So here you get Realm objects on a background thread, and try to access them on the UI thread => Exception.
You should either do everything on the background thread, or everything on the UI thread.
If you're doing a very complex query, i suggest using "findAllAsync" on the RealmQuery, as this will run the query on a background thread, and move them over to the main thread, but it's handled internally by Realm in a safe manner.
for (AppItem item : appItems) {
If appItems contains managed RealmObjects that you obtained from a RealmResults on the UI thread, then accessing them will fail on the background thread.
realm.executeTransactionAsync((realm) -> { gives you a Realm as parameter that is running on Realm's thread executor, so that must be used to obtain managed RealmResults/RealmObjects inside the transaction.
So you need to re-query the objects inside realm.executeTransactionAsync if the objects are managed.
public void execute(Realm realm) {
for (AppItem item : realm.where(AppItem.class).findAll()) {
I have found same error, because it has a different thread.
I solved the problem in a way :
// Repository Class
scope.launch(Dispatchers.Main) {
// Your Code
}
// View Model
viewModelScope.launch(Dispatchers.Main) {
// Your Code
}

Refactoring an standard Android code into Reactive code

So, I am a newbie Android programmer trying to learn RxJava/RxAndroid.
The App I am developing to study has a local database (using Cupboard) that is backed by an API. It has two screens: one showing a list of items and other the items details. Whenever I need to show the items I call this method to load them from my local database and:
public void getItems() {
List<Item> Items = cupboard().withDatabase(database).query(Item.class).list();
if (Items == null || Items.isEmpty()) {
service.listItems().enqueue(new Callback<Items>() {
#Override
public void onResponse(Call<Items> call, Response<Items> response) {
if (response.isSuccessful()) {
cupboard().withDatabase(database).put(response.body().getItems());
mDataReceiver.onItemsLoaded(response.body().getItems());
} else {
Log.d(ItemsLoader.class.getSimpleName(), response.message());
}
}
#Override
public void onFailure(Call<Items> call, Throwable t) {
Log.d(ItemsLoader.class.getSimpleName(), t.getMessage());
}
});
} else {
mDataReceiver.onItemsLoaded(Items);
}
}
If nothing (empty list or null) is returned, I load them from the API, save it in my local database and returns it to the user.
I was trying to refactor this code into reactive (using RxJava, RxAndroid and RxCupboard) by chaining my database calls with the API call, when need, using flatMap but I just couldn't make it work nor wrap my head around it.
Is it possible to refactor this code into Reactive or should I leave this way? If possible, what would be the right way to do this?
This can be solved with a PublishSubject. You can publish data (and errors!) on this object when onResponse() or onFailure() is called. PublishSubject is also an Observable so you can subscribe to its data.
See docs.

Prevent AsyncTask from running multiple times in bindView

I have a custom CursorAdapter that is using multiple AsyncTasks in its bindView method to load images into a grid.
When bindView runs my AsyncTasks get launched multiple times. This has the effect of pushing up my heap size and can cause Out of Memory errors.
What is the best approach to take, to get AsyncTasks to run just once?
You can cache the results of the Asynctask. There is a very useful project on Github you might want to look into.https://github.com/nostra13/Android-Universal-Image-Loader
I think you are looking for AsyncTaskLoader or its support package implementation. It handles everything for you.
An example of how to use it:
public SampleLoader extends AsyncTaskLoader<List<SampleItem>> {
// We hold a reference to the Loader’s data here.
private List<SampleItem> mData;
public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
#Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>();
// TODO: Perform the query here and add the results to 'data'.
return data;
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
#Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbag ecollected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
#Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Begin monitoring the underlying data source.
if (mObserver == null) {
mObserver = new SampleObserver();
// TODO: register the observer
}
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
#Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (mObserver != null) {
// TODO: unregister the observer
mObserver = null;
}
}
#Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
/*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/
// NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized).
// The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
private SampleObserver mObserver;
}
The source: androiddesignpatterns.com - Implementing Loaders (part 3)

Categories

Resources