My entity contains the following private ForeignCollection attribute:
#ForeignCollectionField
private ForeignCollection<Order> orderCollection;
private List<Order> orderList;
What is the best way or usual way to avoid a having a caller use a ForeignCollection? Is there any neat way to return the Collections data to a caller?
How does the following method look? It allows a caller to access the data via a List. Would you recommend doing it this way?
public List<Order> getOrders() {
if (orderList == null) {
orderList = new ArrayList<Order>();
for (Order order : orderCollection) {
orderList.add(order);
}
}
return orderList;
}
If it's ok to change the signature to Collection rather than List, you could try using Collections.unmodifiableCollection().
public Collection<Order> getOrders()
{
return Collections.unmodifiableCollection(orderCollection);
}
Otherwise, your approach of using a lazy member variable is fine (provided you don't need synchronization). Also, note that you can just use the constructor of ArrayList to copy the values from the source collection:
orderList = new ArrayList<Order>(orderCollection);
Related
Actually, I have a Recyclerview where has a button and(where I get id with position[from RestAPi call])--->>when the button is clicked I set another recylerview..and now I want to the from the first RecyclerviewAdapter.
I have already tried global variable
Here is images enter image description here
From my previous answer from another question i think you need a Singleton Pattern rather than a Global Variable
You simply need a getter that returns the another Adapter's ArrayList<SingleItemModel> but the problem you will face is that you need to have the same instance of the Adapter from the Activity in order to get the populated ArrayList<Model>.
A good workaround is to used Bill Pugh's Singleton in the Adapter
public class Adapter {
private ArrayList<Model> list;
private Adapter() {}
public static Adapter getInstance() {
return InstInit.INSTANCE;
}
// Don't forget to set the list (or NPE)
// because we can't argue with a Singleton
public void setList(ArrayList<Model> list) {
this.list = list;
}
// You can now get the ArrayList
public ArrayList<Model> getList() {
return list;
}
private static class InstInit {
private static final Adapter INSTANCE = new Adapter();
}
// Some codes removed for brevity
// Overrided RecyclerView.Adapter Methods
.................
}
Retrieving the ArrayList assuming that the following Adapters are Singleton
AdapterOne a1 = AdapterOne.getInstance();
AdapterTwo a2 = AdapterTwo.getInstance();
ArrayList<Model> a1RetrievedList = a1.getList();
// You don't need to create a new instance
// creating a new instance doesn't make sense
// because you need to repopulate the list
// for the new instance.
ArrayList<Model> a2RetrievedList = a2.getList();
// You can also retrieve from AdapterTwo
Structure
Class Dogs extends RealmObject{
private RealmList<Cats> cats;
}
dogList contains non empty list of cats.
I am trying to remove cats/set as empty list but result also affects on parent list.
protected void removeCatsFromDogs(ArrayList<Dogs> dogList) {
ArrayList<Dogs> newList = new ArrayList<>(dogList);
Observable.fromIterable(newList)
.map(dogs ->
{
dogs.setCats(null);
// dogs.setCats(new RealmList<>()); also not working
return dogs;
}
).toList()
.doOnSuccess(dogs -> baseRealm.executeTransaction(realm -> baseRealm.copyToRealmOrUpdate(dogs)))
.subscribe();
}
doOnSuccess returns dogs with null list but it also reflects in original list.
I have also tried to copy objects one by one using for loop but still same issue.
As stated above, you need to manually create a copy of your objects with the properties you want copied. Something like this should work:
public class Dogs extends RealmObject {
public String name;
public RealmList<Cats> cats;
public static Dogs shallowCopy(Dogs dogs) {
Dogs copy = new Dogs();
copy.name = dogs.name;
return copy;
}
}
protected void removeCatsFromDogs(ArrayList<Dogs> dogList) {
ArrayList<Dogs> newList = new ArrayList<>(dogList);
Observable.fromIterable(newList)
.map(dogs -> { return Dogs.shallowCopy(dogs); })
.toList()
.doOnSuccess(dogs -> baseRealm.executeTransaction(realm -> baseRealm.copyToRealmOrUpdate(dogs)))
.subscribe();
}
You need to create a new Dog in your map() operation. The new Dog will have cats as empty, presumably.
Because you are passing in an object as a parameter, changes the value that is passed in. This is how Java parameter passing works.
This question is a follow-up question from: Organize Android Realm data in lists
Due to the data returned by the API we use, it's slightly impossible to do an actual query on the realm database. Instead I'm wrapping my ordered data in a RealmList and adding a #PrimaryKey public String id; to it.
So our realm data looks like:
public class ListPhoto extends RealmObject {
#PrimaryKey public String id;
public RealmList<Photo> list; // Photo contains String/int/boolean
}
which makes easy to write to and read from the Realm DB by simply using the API endpoint as the id.
So a typical query on it looks like:
realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
This creates a slightly overhead of listening/subscribing to data because now I need to check listUser.isLoaded() use ListUser to addChangeListener/removeChangeListener and ListUser.list as an actual data on my adapter.
So my question is:
Is there a way I can query this realm to receive a RealmResults<Photo>. That way I could easily use this data in RealmRecyclerViewAdapter and use listeners directly on it.
Edit: to further clarify, I would like something like the following (I know this doesn't compile, it's just a pseudo-code on what I would like to achieve).
realm
.where(ListPhoto.class)
.equalTo("id", id)
.findFirstAsync() // get a results of that photo list
.where(Photo.class)
.getField("list")
.findAllAsync(); // get the field "list" into a `RealmResults<Photo>`
edit final code: considering it's not possible ATM to do it directly on queries, my final solution was to simply have an adapter that checks data and subscribe if needed. Code below:
public abstract class RealmAdapter
<T extends RealmModel,
VH extends RecyclerView.ViewHolder>
extends RealmRecyclerViewAdapter<T, VH>
implements RealmChangeListener<RealmModel> {
public RealmAdapter(Context context, OrderedRealmCollection data, RealmObject realmObject) {
super(context, data, true);
if (data == null) {
realmObject.addChangeListener(this);
}
}
#Override public void onChange(RealmModel element) {
RealmList list = null;
try {
// accessing the `getter` from the generated class
// because it can be list of Photo, User, Album, Comment, etc
// but the field name will always be `list` so the generated will always be realmGet$list
list = (RealmList) element.getClass().getMethod("realmGet$list").invoke(element);
} catch (Exception e) {
e.printStackTrace();
}
if (list != null) {
((RealmObject) element).removeChangeListener(this);
updateData(list);
}
}
}
First you query the ListPhoto, because it's async you have to register a listener for the results. Then in that listener you can query the result to get a RealmResult.
Something like this
final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel element) {
RealmResults<Photo> photos = listPhoto.getList().where().findAll();
// do stuff with your photo results here.
// unregister the listener.
listPhoto.removeChangeListeners();
}
});
Note that you can actually query a RealmList. That's why we can call listPhoto.getList().where(). The where() just means "return all".
I cannot test it because I don't have your code. You may need to cast the element with ((ListPhoto) element).
I know you said you're not considering the option of using the synchronous API, but I still think it's worth noting that your problem would be solved like so:
RealmResults<Photo> results = realm.where(ListPhoto.class).equalTo("id", id).findFirst()
.getList().where().findAll();
EDIT: To be completely informative though, I cite the docs:
findFirstAsync
public E findFirstAsync()
Similar to findFirst() but runs asynchronously on a worker thread This method is only available from a Looper thread.
Returns: immediately an empty RealmObject.
Trying to access any field on the returned object before it is loaded
will throw an IllegalStateException.
Use RealmObject.isLoaded() to check if the object is fully loaded
or register a listener RealmObject.addChangeListener(io.realm.RealmChangeListener<E>) to be
notified when the query completes.
If no RealmObject was found after
the query completed, the returned RealmObject will have
RealmObject.isLoaded() set to true and RealmObject.isValid() set to
false.
So technically yes, you need to do the following:
private OrderedRealmCollection<Photo> photos = null;
//...
final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<ListPhoto>() {
#Override
public void onChange(ListPhoto element) {
if(element.isValid()) {
realmRecyclerViewAdapter.updateData(element.list);
}
listPhoto.removeChangeListeners();
}
}
Is it a good idea to pass a list of object from one Activity to another for Android ?
It is quiet troublesome to pass a list of object in an intent, and I wonder whether it affect the performance if the object list is too large or the object is too complicated.
Is it a better solution to get the list of object from other place, for example, query the DB once more , or save the list of object in a temporary class and fetch it in new Activity?
As long as you are passing Parcelable objects' list, nothing's wrong when passing through Intent. Regarding performance, that is up to you and the amount of data you pass.
As per my experience, If you are passing data up to 1MB of size, it should be fine. Anything above that will fail as this seems to be the limit. Read this.
Besides, you are welcome to use preferences, SQLite, files or static-object-referencing methodologies to fetch your data anytime and anywhere.
Solution1 : Use Intent
Send :
Intent data = new Intent(Activity1.this,Activity2.class);
data.putParcelableArrayListExtra(Constant.LIST_OBJECT,
(ArrayList<? extends Parcelable>) getObjects());
receive :
List<YOUR_OBJECT> objects = data.getParcelableArrayListExtra(Constant.LIST_OBJECT);
Solution2 :
public class SessionData {
private static SessionData instance;
private final List< YOUR_OBJECT > listSessionObjects;
private SessionData() {
listSessionObjects = new ArrayList<>();
}
public static final SessionData getInstance() {
if (instance == null) {
instance = new SessionData();
}
return instance;
}
public List<YOUR_OBJECT> getListSessionObjects() {
return listSessionObjects;
}
public void setListSessionObjects(List<YOUR_OBJECT > objects) {
listSessionObjects = objects
}
}
to use it :
SessionData.getInstance().getListSessionObjects();
SessionData.getInstance(). setListSessionObjects(objects);
I was thinking. What is the best way to save a custom adapter before activity onDestroy() is called? I want to populate an adapter with items (texts and images) and set it to listView. However, I don't want to repopulate the adapter again when the user navigate away from that activity and comes back as repopulation is too time consuming. I want to save the adapter value somewhere before the activity inDestroy() is called and check if it empty on activity onCreate.
Well, adapter is a pretty complex object and its persistent saving may be a difficult task (if possible at all). The more common approach is saving persistently your dataset.
You worry about the population time, but serialization-deserialization of the adapter is going to take time as well, and apparently much more time then the dataset alone, because it includes the dataset in it.
EDIT
Small conceptual example on saving your dataset to SharedPreferences using Gson library (more on it) (just one of the ways to persistently save your data):
public void saveData(ArrayList<YourDataType> data) {
Gson gson = new Gson();
String dataJson = gson.toGson(data);
getSharedPreferences("your_prefs", Context.MODE_PRIVATE).edit()
.putString("key_data", dataJson)
.apply();
}
public ArrayList<YourDataType> restoreData() {
String dataJson =
getSharedPreferences("your_prefs", Context.MODE_PRIVATE)
.getString("key_data", "empty");
ArrayList<YourDataType> data = null;
if (!dataJson.equals("empty")) {
Gson gson = new Gson();
Type collectionType = new TypeToken<ArrayList<YourDataType>>() {}.getType();
data = gson.fromJson(dataJson, collectionType);
}
return data;
}
You can save adapter data inside application class and when activity is recreated check Application class arraylist is empty or not. If not empty
assign it to adapter.
But storing it inside global variable i.e Application class may make your app heavy on heap memory.
public class GlobalState extends Application {
ArraList<Type> arrayList = new ArrayList<type>();
public void setArraylist(ArraList<Type> arrayList) {
this.arrayList = arrayList;
}
public ArraList<Type> getArrayList() {
return arrayList;
}
public int dataSize() {
return arrayList.size();
}
}