Insider's Guide to Udacity Android Developer Nanodegree Part 7 - Full Stack Android |
Written by Nikos Vaggalis |
Monday, 23 April 2018 |
Page 5 of 8
GSON ProblemsSo back to the WebSite and in connecting to http://smadeseek.com/tabletdetails?id=858 to retrieve a device's detailed specifications, the WebSite's TT template would, amongst other UI elements, render a HTML text view with device's id 858, Color values. The data structure that the template works with is an Array of Color values which is attached onto a hashref's 'ColorName' key: { 'ColorName' => [ 'black', 'white', 'grey', 'red', 'blue' ], } However in case there's just a single value, no Array is used: { 'ColorName' => 'black' } Template Toolkit handles both of the cases without issues upon rendering the HTML
because : "The FOREACH directive will iterate through the items in a list, But the equivalent Android call to http://smadeseek.com/tabletdetails/json?id=858 would sometimes have the GSON parser choke with 01-27 23:24:56.865 24052-24052/nvglabs.android.com. This happens when it receives a single value 'ColorName' : 'black' in place of an array 'ColorName' : [ 'black', 'white', 'grey', 'red', 'blue ]
because the GSON Deserializer expects to map the JSON to the following Java class structure
to make a Java object that represents the device together with its specifications.In other words it expects a List of color strings not just a single color string value. Unlike TT, under Java's strong type system we can have one or the other, a String or a List<String>, not both.So when the Deserializer encounters a single string value in place of the List of strings, it doesn't know what to do with it hence the exception. To solve this, the first option was to redesign the Model component on the server's side so that it always returns JSON arrays.That would however mean amending the site's design too.The second and easier one was to leave the server side as-is without any modifications and make the necessary changes on the client's side.This in effect meant customizing the way GSON deserializes the JSON feed.
For that I had to tap into the JSON tree structure the deserializer sees and make the necessary amendments to the offending nodes.So in this case when the deserializer finds a 'ColorName' node, jsonObject.get("ColorName") != null which doesn't point to a null value, !jsonObject.get("ColorName").toString().equals("null") and that value is not an Array, !jsonObject.get("ColorName").isJsonArray() it means that 'ColorName' points to a non null single string value, as in 'ColorName' : 'black'. If that's the case I immediately create an array with that value ('black') as its only element.Then I attach this array to the JSON tree in place of the bad node, that is, I remove the old one with jsonObject.remove("ColorName") and attach the modified one with jsonObject.add("ColorName",x). Although I've fixed the troublesome nodes, at the same time I wanted the trouble-free nodes to be parsed as they initially were set to.Therefore at the end of the deserializer's block, I run the default deserialization again but this time on the newly created tree that contains all the nodes, modified or not:
Working with Fragments and the BackstackOnce in the Listing screen:
there's a number of paths you can follow, according to the application's design:
This diagram clearly demonstrates how the use of Fragments leads to modularized design and thus reusability.However in working with Fragments, you ought to possess a deep understanding of how their lifecycle is utilized, something that is further complicated when the Backstack comes into play. For example, a common misconception is that popBackStack() pops fragment instances off the stack.In reality it pops transactions which could had involved a number of fragments.Yet another, is not being aware that when such a transaction adds an instance of a fragment to the backstack, the backstack acts as container that preserves the fragment's state by keeping the fragment's member variables in tact.T The Listing screen (FragmentListDevices) uses this technique when it comes on top of the backstack after returning from the Filter screen (FragmentFilter), that is, after FragmentFilter is popped off the backstack as in FragmentListDevices<--FragmentFilter. So upon returning to FragmentListDevices its onCreateView is called again, therefore in there I check the mRecyclerView member field, the one that points to the actual RecyclerView instance, for nullity.If it's not, that means that the rest of the Views have been preserved too thus I don't have to go through creating them again!Instead, I just return the stored rootView
However the same trick won't save the day when the device is rotated.At this point you have to check for savedInstanceState != null, retrieve the saved state from savedInstanceState and with it recreate the RecyclerView from scratch. Another property of Fragments is that Fragments in essence do not die and as such you can reconnect to them and call their methods (setMenuValues() in this case) by retrieving their instance through the Activity's FragmentManager :
Scrolling, paging and EventBusPaging was handled manually hence painfully because the website sends over the list of devices in batches of 9.Therefore the app had to be adapted so that when the RecyclerView is scrolled upwards and there's more devices to be fetched, RecyclerView's OnScrollListener() event handler should trigger an event, EventBus.getDefault().post(scrolling).This in turn is intercepted by
}
which restarts the Loader.The Loader as already gone through, initiates a new network request in order to fetch the next batch of 9. If it wasn't for Eventbus, the alternative would be just ugly; global variables and tedious control mechanisms in order to make it work.Yet another great use of Eventbus was in facilitating Fragment to Activity communication in place of the classic Interface-Listener pattern. |
Last Updated ( Monday, 23 April 2018 ) |