Let's say we have the stock of items stored in the JSON txt file. To access the data we load JSON file using a stringBuilder class, then populate StokItems into:
List<StockItem> stock;
like this:
stock.clear();
Gson gson = new Gson();
stock = gson.fromJson(stringBuilder.toString(), new TypeToken<List<StockItem>>() {
}.getType());
as we would like to take advantage of the LiveData in Android we introduced:
MutableLiveData<List<StockItem>> stockLiveData = new MutableLiveData<>();
To make it working we have to post data into it:
public LiveData<List<StockItem>> getLiveStockList() {
stockLiveData.postValue(stock);
return stockLiveData;
}
So here is the question, if the line:
stockLiveData.postValue(stock);
consumes memory for creating another copy of
List<StockItem> stock
and if so, could be it's a good idea to keep "original data" inside the
stockLiveData
variable only to reduce the memory impact? (If is it possible ...)
No need to use global variable to hold temporary data. either you can use local variable or directly post to LiveData.
public LiveData<List<StockItem>> getLiveStockList() {
return stockLiveData;
}
And then either
List stock = gson.fromJson(stringBuilder.toString(), new TypeToken<List<StockItem>>() { }.getType());
stockLiveData.postValue(stock);
or
stockLiveData.postValue(gson.fromJson(stringBuilder.toString(), new TypeToken<List<StockItem>>() { }.getType()))
You can also access the list inside LiveData like below:
stockLiveData.getValue();
Which return the original list data.
There are couple of things about your code I want to point out here:
You do not necessarily need to call postValue in your getter function. As far as there are observers observing from your getter function, you can post value from anywhere.
Even if you use mutable live data that does not necessarily mean that you're allocating memory for or creating a copy of List. When you set or post value on your Mutable Live Data, you're basically referencing the existing list on to value in your Mutable Live Data.
Related
I recently switched to Android MVVM on one of my Projects, the problem that I am facing is that with my Fragments used with ViewPager and TabLayout the data for each tab must be different based on an id of each tab, however since I am using AndroidViewModel to connect to my data source, the same data is shown in all my tab fargments. I understand that problem is that same ViewModel is shared between all the dynamic fragments[Fragmnet Class being the same].
Is there any way around this? or if I am doing something wrong.
//Code that returns data
private MutableLiveData<List<InventoryProduct>> inventoryProductList;
//we will call this method to get the data
public LiveData<List<InventoryProduct>> getCategoriesList(String cat_id,String store_id) {
//if the list is null
if (inventoryProductList == null) {
inventoryProductList = new MutableLiveData<>();
//we will load it asynchronously from server in this method
loadInventoryProducts(cat_id,store_id);
}
//finally we will return the list
return inventoryProductList;
}
There is nothing wrong with having the same ViewModel for multiple fragments, in fact, it helps in a lot of ways. In your case, I would suggest that keep some identifier in the fragment, which you can pass to the ViewModel's function and accordingly decide what data to provide. This way different fragments would have different data and your data would still be persistent as long as the LifeCycleOwner is alive.
Following from the edited question, you will need to remove the null check, as the same instance of ViewModel is being used, once initialized, inventoryProductList is never null again and hence the subsequent functions are getting the data of the first fragment. As a solution(If you don't want to go the DB way), you can maintain a Map of the LiveData like this
Map<CatId/StoreId,LiveData<List<InventoryProduct>>> dataMap=new HashMap();
Now Instead of null check, you check the map for your CatId/StoryID (based on what key you have already used) and if the Map does not have the value already, go for the API call, otherwise return the value from the map.
Something like this
Say you had used StoreID as the Key
if(!dataMap.containsKey(store_id)){
MutableLiveData<List<InventoryProduct>> inventoryProductList = new MutableLiveData<>();
//we will load it asynchronously from server in this method
loadInventoryProducts(cat_id,store_id);
dataMap.put(store_id,inventoryProductList);
//You need to post the response from the api call in this inventoryProductList
}
return dataMap.get(store_id);
Make sure that once you get the API response for the corresponding cat_id/store_id, you actually post the data to the corresponding LiveData.
I have a data class named "DATA" then I made an ArrayList variable like this:
var listData : ArrayList<DATA>
but I need to change a variable in a certain position but I don't know how to change it. If in Java we can just use setter and getter. How to change a specific variable form data class?
First (sorry I have to stay this) classes shouldn't be all caps :-) Usually, in naming conventions, this is applied to constant values. Classes are named with the first letter as capital only (just a recommendation if you are ever going to apply for pro android dev position.). Second, data classes are like much simpler Java's AutoValue generated objects. Their values are immutable on purpose. You can't change a value inside a data class. It is not possible in Kotlin. If there are changes needed in values inside data classes, they usually happen while mapping to another data class. Let's say when mapping a network deserialised data class object to domain layer data class object. (DataResponse -> Data). So if you need to change it, you can allocate the value of the data class to some local variable and then change it, or map it to another data class.
It looks like you want to insert a new data in a specific position in the array?
Given you have your data class defined like this:
data class MyDataClass(var listData : ArrayList<Data>)
class Data
You just need to:
val myDataClass = MyDataClass(arrayListOf())
val someNewData = Data()
val index = 0
myDataClass.listData[index] = someNewData
Try this,
var dataObject = DATA();
dataObject.listData[index] = "Your data";
I have RealmResults that I receive from Realm like
RealmResults<StepEntry> stepEntryResults = realm.where(StepEntry.class).findAll();
Now I want convert RealmResults<StepEntry> to ArrayList<StepEntry>
I have try
ArrayList<StepEntry> stepEntryArray = new ArrayList<StepEntry>(stepEntryResults));
but the item in my ArrayList is not my StepEntry object, it is StepEntryRealmProxy
How can I convert it?
Any help or suggestion would be great appreciated.
To eagerly read every element from the Realm (and therefore make all elements in the list become unmanaged, you can do):
List<StepEntry> arrayListOfUnmanagedObjects = realm.copyFromRealm(realmResults);
But you generally have absolutely no reason to do that unless you want to serialize the objects with GSON (specifically, because it reads field data with reflection rather than with getters), because Realm was designed in such a way that the list exposes a change listener, allowing you to keep your UI up to date just by observing changes made to the database.
The answer by #EpicPandaForce works well. I tried this way to optimize my app performance and I find the following is a bit faster. Another option for people who prefer speed:
RealmResults<Tag> childList = realm.where(Tag.class).equalTo("parentID", id).findAll();
Tag[] childs = new Tag[childList.size()];
childList.toArray(childs);
return Arrays.asList(childs);
In Kotlin:
var list : List<Student>: listof()
val rl = realm.where(Student::class.java).findAll()
// subList return all data contain on RealmResults
list = rl.subList(0,rl.size)
I have a recyclerview which shows a list of students.
All of the students are held in a realmlist in the adapter.
When the user can ask for a data refresh, than the server sends the list of students back to the user.
What i am doing now is to download all of the information from the server , store it in the db , than retrieving it from the database(via realmresults) and than converting the realmresult to realmlist.
My question is how to properly update the UI?
I have seen in the documentation that realmlist have a managed mode where they are updating the ui automatically..
What is this managed mode? What does it means?
How do i use the realmlist to keep it in a managed state?
And what is the right way(aka best practice) to use realmlists?
Note that i cannot hold my information as realmresult directly because im performing some manipulation on the data that i dont want it to be saved.
Managed Object vs. Standalone
The standalone RealmObject/RealmList is created through the Object's constructor or the Realm.copyFromRealm() method. The data accessing in the standalone object won't go through the underline storage engine, instead, it behaves just like normal object. So the standalone object won't be refreshed when data changes. Examples for standalone object:
MyModel myModel = new MyModel(); // Standalone
MyModel model = realm.where(MyModel.class).findFirst(); // This is managed object.
MyModel standaloneModel = realm.copyFromRealm(model); // The returned value is standalone object.
MyList myList = new MyList(); // Standalone
The managed RealmObject/RealmList are accessing data though Realm's underlying storage engine. They are created when you do query from Realm, or the return from the copyToRealm() (and its variant methods). Like:
MyModel model = realm.where(MyModel.class).findFirst();
MyModel model = new MyModel(); // This is a standalone object.
model = realm.copyToRealm(modle); // The returned value is managed by Realm now.
MyList myList = realm.where(MyModel.class).findFirst().getMyList();
How to properly update the UI
The suggestion is using Realm's change listeners. See https://realm.io/docs/java/latest/#notifications
And what is the right way(aka best practice) to use RealmList?
This is a bit confusing, but Realm is introducing RealmCollection into the next major release (v0.89.0). See https://github.com/realm/realm-java/pull/2345.
Note that i cannot hold my information as realmresult directly because im performing some manipulation on the data that i dont want it to be saved.
If the RealmList is in managed mode, the data changes will be saved as well. If you don't want some data to be saved, you could consider to use #Ignore annotation on those fields. See https://realm.io/docs/java/latest/#ignoring-properties
Update on 04072016
RealmList vs. RealmResults:
RealmList is a list of RealmObject saved as a field of a RealmObject. It represents the one-to-many relationship in Realm.
RealmResults is the results of query.
Both of them (if RealmList in managed-mode) will be auto-refreshed when data changes.
You can get a RealmResults from a RealmList by RealmList.where() like:
RealmResults results = myRealmList.where().findAll();
This answer will be a bit out-of-date after Realm v0.89.0 released, because of new RealmCollection.
THE SCENARIO
I have a class that makes use of a request list set by the user. The request list is stored in SharedPreferences. The dilemma I'm facing is to whether to keep an instance of the request list or to read from SharedPreferences every time the request list is needed (which is very frequent).
Also not that Gson is used to deserialize the object.
The code goes like this:
public List<PrayerTimesCalculator.Time> getDefaultRequestList() {
if (mRequestList != null) return mRequestList;
// Try getting request list from preferences;
Gson gson = new Gson();
String json = mSharedPref.getString(KEY_PREF_REQUEST_LIST, null);
Type listType = new TypeToken<List<Time>>() {
}.getType();
mRequestList = gson.fromJson(json, listType);
if (mRequestList != null) return mRequestList;
// Create default list;
mRequestList = Arrays.asList(
Time.DAWN,
Time.MORNING,
Time.AFTERNOON,
Time.EVENING,
Time.MID_NIGHT);
return mRequestList;
}
THE GOAL
My concern is that if I keep around an instance of the request list, and there are multiple instances of this class, an update to the request list in one instance of the class would not be reflected in the rest of the instances until they are recreated.
Thus, I'm leaning towards reading from SharedPreferences unless there is a better way to keep the request list objected updated in all instances.
THE QUESTION
(1) So, how efficient is it to read the same key from SharedPreferences quite frequently by multiple instances of the object? and (2) Is there a better way to keep the request list objected updated in all instances?
So there are a couple of approaches you can take to this.
First, your object is small - re-reading SharedPreferences thousands of times would hardly be noticeable. It's not like SharedPreferences is on a remote drive or has a "bad connection."
Second, if you don't like that answer, then you need a DAO (Data Access Object). SharedPreferences is a form of this already. It provides a means to store and retrieve data with confidence that you have the most recent data available. But, if you feel like you can improve on it's optimization (because it's generic, and this is your app), then you can provide access to you data through a static object that performs both "read" and "write" operations. This will guarantee that access to the object is done with the most recent data. Of course, you will need to be thread aware, etc. (something that is not always guaranteed by SharedPreferences).
Next, you could persist your data in a database and use Cursors or other built-in or custom DAOs. This requires another level of complexity and a lot of overhead, but is useful when several components of your app might need access to the data, provide updates or needs real-time monitoring of changes because background threads or other objects may make modifications that will change your app behavior or result in UI updates.
Last, you could use more complex data stores like a Content Provider. This is really required for cases where you want/need other apps to access data provided by your app (and your app may also consume the data). That's a complex solution and implementation is well outside the scope of this question.
But I mention it because you seem interested in being certain that frequent reads of SharedPreferences is acceptable. It definitely is acceptable - otherwise there would be something else besides it, databases and Content Providers.