I have a RecyclerView that stores items from an API. The problem is that the objects returned from the API shift around based on users interacting with them.
So let's take an example, let's say I make a request to the API to grab a list like so:
{
"startTime": "2016-02-01 01:20:60",
"endTime": "2016-02-01 01:00:60",
"objects": [
{
"id": "4",
"image": "http://image.com/1",
"type": "image"
},
{
"id": "3",
"text": "Something something",
"type": "post"
},
{
"id": "2",
"text": "Something else",
"type": "post"
},
{
"id": "1",
"text": "Something",
"type": "post"
}
]
}
Ok that's stored in the list, NOW the users hits SwipeToRefresh and this is returned back from the API:
{
"startTime": "2016-02-01 01:30:60",
"endTime": "2016-02-01 01:20:60",
"objects": [
{
"id": "8",
"image": "http://image.com/10",
"type": "image"
},
{
"id": "7",
"text": "Something new",
"type": "post"
},
{
"id": "3",
"text": "Something something",
"type": "post"
},
{
"id": "4",
"text": "Something something",
"type": "post"
}
]
}
Notice that Posts with ids of 3 and 4 are returned back in a new list! We need to somehow remove the older items and update the list with these items gracefully.
What is a good design to implement this? Is there anything clean and robust?
My initial hunch would be to:
adapter.update(List<Object> newObjects);
// Later on in adapter...
void update(List<Object> newObjects) {
// Add items to the top of the list
// Sort through old list and if ids match newer items remove them
loop {
// Call notifyItemRemoved
}
}
Any better thoughts on this? By good design I mean updating the list without the user taking notice of any jankiness or lag.
Please read the question carefully before downvoting or answering, this may sound like a simple question. It's not.:
Specifically I am looking for answers that deal with effective memory allocation, a simple clean fluid interface and easily debuggable.
An ideal solution would probably be a base 'abstract' 'RecyclerViw.Adapter' that holds the logic for updating / removing / modifying a 'ViewHolder' with thread safety. Assume I am already using libraries to handle JSON / POJO serialization and Client-side REST.
The items in my RecyclerView get shifted around in a list reflected from the list returned from the API. Items move around and are dynamic when the user is refreshing the view from the top or loading more views from the bottom when scrolling down. Items are not static in a list their positions will change depending on users interacting with those items as reflecting from the list returned from API.
If you want nice and fluid design, I would create a Sqlite Database for your app. Store the data in Database that you get from the web api call. Every time user swipes down for new data then parse JSON and update the database. Do this using AsyncTask, service or IntentService in the background thread and not on Main(UI) thread.
Now hook up your RecyclerView with your stored items in the database using CursorLoaders. Of course you would have to do a little more work this way but it would be worth it at the end.
hope this would be a good starting point.
I would use retrofit for getting my objects from api, then I would create a Gson model for the object returned by your api. Something like:
class Model{
private String startTime;
private String endTime;
private Post[] objects;
class Post{
private String id;
private String text;
private String type;
// get/set methods
}
// get/set methods
}
And then I will use rxAndroid for modifying my list.
See here on how to use rx with retrofit. I would suggest studying flatmap of rxjava for parsing your objects in Post[]. I'm not an expert in rx so I can't give an example yet.
Related
I am using Retrofit with GSON for an app for a client, and I am having some trouble with some of my client APIs and I need to workaround this problem.
Let's say i have an API which gives me telephones:
{
"telephones": [
{"phoneNumber": "1234567890"},
{"phoneNumber": "2123456789"}
]
}
But my client decided if there is only one telephone i am sending you:
{
"telephones":
{"phoneNumber": "1234567890"}
}
And when there is no telephone:
{
"telephones": "No telephone Available"
}
Is there any workaround i can make with Kotlin to solve this datatype problem?
In iOS I could force them reimplementing the Coding method and force them to always have an array. Is it possible to do something similar in Kotlin?
This is a small example, since the original answer has between 600 and 1300 lines of JSON data.
This might work.
I have done this in many places in my app.
So, the first thing, let's say you receive multiple different telephone numbers in one JSON file and might look something like this.
install a plugin call JSON to Kotlin Class
once done, make a new file using "Kotlin data class file from JSON"
the plugin for me creates automatically appropriate files.
then I use the main file to capture the data, this has worked for me almost everytime.
And yes, my other answer was if you are doing everything manually, while retrieving the data make a data class such as:
This is just for explanation purposes.
//lets say your json looks something like this
"records": [
{
"id": "1",
"telephones": [
{"phoneNumber": "1234567890"},
{"phoneNumber": "2123456789"}
]
},
{
"id": "2",
"telephones":
{"phoneNumber": "1234567890"}
},
{
"id": "3",
"telephones": "No telephone Available"
}
]
my example code would look something like this.
data class records(
//some id
val id: Int,
var telephones: List<Long>
)
so, now check inside telephones how many elements are there and add them to the list one by one.
Is it possible to convert this :
{
"news": [
{
"nid": 1,
"type": "test1",
},
{
"nid": 2,
"type": "test2",
}
]
}
To this when I receive the result with Retrofit ?
{
"nid": 1,
"type": "test1",
},
{
"nid": 2,
"type": "test2",
}
Actually in my model, I have a NewsList object with a List of News inside (with value inside : nid, type).
And the Retrofit query looks like this :
#GET("test/test.json")
suspend fun getListNews(): Response<Newslist>
Everything is working fine, but now I need to save that data in a database with Room and it will be easier if the Retrofit query was like this :
#GET("test/test.json")
suspend fun getListNews(): Response<List<News>
What is the best way to achieve this ?
Convert Json response to get something like above ?
Keep things like this and Convert the NewsList to be able to be stored in the Room Database ?
The best way is to use RXJava or Coroutine of Kotlin.
In RXJava you can use map operator to transform your response object to list of objects.
Please refer below link to get some idea of using RXJava with retrofit and transforming results:
https://www.journaldev.com/20433/android-rxjava-retrofit
The best way is to have your backend server return the canonical json data.
If this is not possible, you can customize the converterFactor by addConverterFactory method,compatible with this data format in a custom converterFactor.
first, you must to have one layer for data provider to provide for consumer (activity, VM, fragment, etc).
in this layer, you have use case to define data collection from Cloud(Retrofit) or from Local Storage(Room). more info, i have sample here
Yes, you can, using data classes, you just need to create a data class called news with a list of items with "nid" and "type", in that cases I use a plugging of Android Studio Called Kotlin data class File from JSON
I think the better way is to return NewsList object, because it makes you easier to compatible with new feature update, like adding more fields to NewsList object. You can create a new layer for saving your data to room database, and convert the raw data to List for saving it.
Let me start by saiyng that I'm a complete beginner with Android and Firebase too. I'm using a small project to learn booth technologies and I came across with this challenge.
Imagine I have the following Firebase Database structure:
{
"projects": {
"p01": {
"name": "Projecto #1",
"created_at": "2017-01-01 20:00:00",
"tags": {
"t01": true,
"t03": true
}
},
"p02": {
"name": "Projecto #2",
"created_at": "2017-02-01 02:20:33",
"tags": {
"t02": true,
}
}
},
"tags": {
"t01": {
"color": "#000CCC",
"name": "Nature"
},
"t02": {
"color": "#DD00FF",
"name": "Technology"
},
"t03": {
"color": "#438DC2",
"name": "Social"
}
}
}
I want to display a list view with all my projects, and on each row I want to display the title of the project and a list of tags with its background color. That I managed to too, but I think the way I achieved it, its not the best practice.
First I create a value event listener to get all all projects, like this:
fbdRef.child("projects").addValueEventListener(new ValueEventListener() {
And on the onDataChange I have a for loop that reads and adds all the projects to an ArrayList, but in that loop I do something first asyncronously. Something like this:
for(DataSnapshot tagSnapshot: projectSnapshot.child("tags").getChildren()){
fbdRef.child("tags/" + tagSnapshot.getKey()).addValueEventListener(new ValueEventListener() {
I'm reading all tags, one by one, and updating its item on ListView, this looks ugly and it may be slow. I really am struggling with noSql concept of Firebase and the best way to structure my databases.
What is the best way to get all tags content from each project?
The direct alternative is to:
reference.child("projects").addChildEventListener(...);
Then for each child added, or moved, or remove, pass it to the adapter and update the adapter.
Since you probably don't want to handle every situation, there is Firebase-Ui library which provides an adapter to display the list: https://github.com/firebase/FirebaseUI-Android
You should also check your data structure since the tags are outside the element itself you are doing 2 queries, you should simply add the "name" and "color" attribute to the Project model. The key for structuring data in Firebase is to do it by thinking what the views need. Avoid unnecessary nesting and extra queries.
After successfully receiving the first request for information through the Graph API, I attempt to get the next page through the provided public GraphRequest getRequestForPagedResults(PagingDirection direction) method in the com.facebook.GraphResponse class.
However, I keep on getting null as a result on the line JSONObject pagingInfo = graphObject.optJSONObject("paging"); despite the returned JSONObject looking like the JSON code below.
{
"id": "10100476747286781",
"posts": {
"data": [
{
"id": "123123123"
...
}
],
"paging": {
"previous": "https://graph.facebook.com/v2.5/10100476747286781/posts?limit=200&since=1448931408&access_token=CAAM6MhXVsZAYBAN0tW33gMbwnWhs9HtZChlqsGwjgoR2IB9kZCej3pLS8dZCIOSsufYlVlHtJdkOZAHpr0bsPtZAmfj6ZAiXQ9zTXTe9lUghAuXnSQhZBM6YQfRPy26UfXbp4IQe9gKhG50qUZCURtOFAral1NqO8aIoAZCpRZBthp435HCo4uiZA7LqOIK7vxyT6MJ7e3nzcHyOhBDSSaqWYm1L9xUGzmml8Gg6TCZAzUupZCZBwZDZD&__paging_token=enc_AdCrwK4mXgYPS2XHW9Vjgb0ydGnENZCVb8cdyRGdPidfcQAc1573AWMVKR0DNZBzQmxg5ndkZAHfZAvWSpK8UFcG2SBZA&__previous=1",
"next": "https://graph.facebook.com/v2.5/10100476747286781/posts?limit=200&access_token=CAAM6MhXVsZAYBAN0tW33gMbwnWhs9HtZChlqsGwjgoR2IB9kZCej3pLS8dZCIOSsufYlVlHtJdkOZAHpr0bsPtZAmfj6ZAiXQ9zTXTe9lUghAuXnSQhZBM6YQfRPy26UfXbp4IQe9gKhG50qUZCURtOFAral1NqO8aIoAZCpRZBthp435HCo4uiZA7LqOIK7vxyT6MJ7e3nzcHyOhBDSSaqWYm1L9xUGzmml8Gg6TCZAzUupZCZBwZDZD&until=1334148469&__paging_token=enc_AdBEPCJpDZALodXcvmWUJy4rV4mQlFsHFNI8qNlvvVXGYAcZAkB8ZB1i1LRVKZCJND6j71MrINp1FKUDTTgQPZCTEU2t7"
}
}
}
Why doesn't optJSONObject search deeper into the JSON tree?
Is there an interface which I can use to directly use the URL provided in the JSONObject data?
i.e.
"next": "https://graph.facebook.com/v2.5/10100476747286781/posts?limit=200&access_token=CAAM6MhXVsZAYBAN0tW33gMbwnWhs9HtZChlqsGwjgoR2IB9kZCej3pLS8dZCIOSsufYlVlHtJdkOZAHpr0bsPtZAmfj6ZAiXQ9zTXTe9lUghAuXnSQhZBM6YQfRPy26UfXbp4IQe9gKhG50qUZCURtOFAral1NqO8aIoAZCpRZBthp435HCo4uiZA7LqOIK7vxyT6MJ7e3nzcHyOhBDSSaqWYm1L9xUGzmml8Gg6TCZAzUupZCZBwZDZD&until=1334148469&__paging_token=enc_AdBEPCJpDZALodXcvmWUJy4rV4mQlFsHFNI8qNlvvVXGYAcZAkB8ZB1i1LRVKZCJND6j71MrINp1FKUDTTgQPZCTEU2t7"
Personally, from my experience its better to access Facebook using REST API's instead of using their official SDK. It gives me a lot more freedom on how to handle the request-response and skip over any limitations which are part of the SDK.
Additionally, I can use my own networking layer of Volley/OkHttp which makes handling requests and threads much more easier and efficient.
I have been using Volley to make requests to my api. Everything has been going great so far. I deserialize the JSON response and cache the object into my db, then query my db to show the object's data. But, what if my response is something like this:
{
"Author": {
"name": "John Doe",
"Books": [
{
"url": "www.myapi.com/book/1"
},
{
"url": "www.myapi.com/book/2"
},
{
"url": "www.myapi.com/book/3"
}
],
"Articles": [
{
"url": "www.myapi.com/article/1"
}
]
}
}
The urls are the api endpoints to the actual objects. To get all of the information that I need for my views, I will have to do a for loop and make 4 more api requests to get by Books and Articles objects. I am not sure what the best way is to accomplish this. I can't query my database until the requests have completed, and there isn't a way to know when the last request has finished.
This seems common, but I haven't come across anything yet that deals with this type of situation. How can this be done?
The response is bad for UX because you need to request N times before you can show something to the user.
If you can change the response, I suggest you do it.
By the way, you can use EventBus to send an object to the main thread from a Volley thread, so you can update the UI each time you finish a request.