Dec 29 | 8:55 AM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Chandra S. | has entered the room |
Mark M. |
hello, Chandra!
|
Mark M. |
how can I help you today?
|
Chandra S. |
Hi, Mark
|
Chandra S. |
How to call getLayoutInflater from onCreateViewHolder if we separate the Adapter class in the different file?
|
Mark M. |
either pass in the Activity into the RecyclerView.Adapter, or pass in a LayoutInflater into the RecyclerView.Adapter, via a constructor or setter
|
Dec 29 | 9:00 AM |
Chandra S. |
I didn't the second one: " pass in a LayoutInflater into the RecyclerView.Adapter, via a constructor or setter"
|
Chandra S. |
*didn't get
|
Mark M. |
well, your activity or fragment is creating the RecyclerView.Adapter
|
Mark M. |
your subclass of RecyclerView.Adapter can have a constructor or setter, that accepts a LayoutInflater, and holds onto that inflater in a field
|
Mark M. |
your activity or fragment can call getLayoutInflater() and pass that LayoutInflater to that constructor or setter
|
Chandra S. |
Ah, I see..
|
Chandra S. |
Is it better to separate or to combine in 1 file?
|
Mark M. |
I cannot really answer that
|
Mark M. |
either could be "better"
|
Chandra S. |
Ok.. thanks..
|
Chandra S. |
Another question
|
Chandra S. |
If I want to put an EditText in every row of RecyclerView, how I get that input?
|
Mark M. |
what I have been using combines two things
|
Dec 29 | 9:05 AM |
Mark M. |
first, I override onViewDetachedFromWindow(), which tells me that one of my ViewHolder's views is no longer going to be used, so there I grab whatever was in its EditText and hold it in my model/view-model/whatever object
|
Mark M. |
however, that does not cover those ViewHolders that are still "alive" at the time (e.g., at the point the user triggers a configuration change, where I want to save data in the saved instance state bundle)
|
Mark M. |
so, second, at those points where I need to make sure that I am holding the latest-and-greatest information of all the EditText widgets, I iterate over the adapter positions and call findViewHolderForAdapterPosition() on the RecyclerView
|
Mark M. |
that method returns null if there is no "live" ViewHolder for that adapter position, or the ViewHolder if there is one
|
Mark M. |
and I then get the latest text from the EditText from whichever ViewHolders I get
|
Mark M. |
I am not completely confident in this approach, though it has been holding up so far
|
Mark M. |
in principle, you could use TextWatcher with each of the EditText widgets and update your model/view-model/whatever whenever the text changes, but this adds some overhead
|
Dec 29 | 9:10 AM |
Chandra S. |
Many things I see for the first time :-) Thanks, Mark, I'll try to understand after the chat over
|
Chandra S. |
If I want to put Button, the easier approach can be used right?
|
Mark M. |
yes, just attack a click listener to it
|
Mark M. |
the same thing goes for CompoundButton implementations
|
Mark M. |
there, the input is atomic: it's a click, or a checked-state change, or something
|
Mark M. |
EditText has complex input
|
Mark M. |
the closest analogy to calling setOnClickListener() on a Button would be adding a TextWatcher to an EditText
|
Mark M. |
and, again, that's a possibility, particularly if you need real-time on-the-fly updates to the data
|
Mark M. |
but, it adds overhead per keystroke
|
Ace R. | has entered the room |
Ace R. |
Hi all
|
Chandra S. |
I see.. OK, thanks, Mark..
|
Mark M. |
Chandra: let me take a question from Ace, and I will be back with you shortly
|
Chandra S. |
Hi Ace, we meet again :-)
|
Mark M. |
Ace: your turn! do you have a question?
|
Dec 29 | 9:15 AM |
Ace R. |
View paste
|
Mark M. |
well, with Retrofit 2.x, you have to implement onResponse() and onFailure()
|
Mark M. |
on your Callback that you supply to enqueue()
|
Mark M. |
I have assumed, possibly incorrectly, that a non-200 response would trigger onFailure()
|
Ace R. |
okay i currently have something like this
|
Ace R. |
View paste
(8 more lines)
|
Mark M. |
right
|
Mark M. |
so, the issue is: under what circumstances is onFailure() called?
|
Ace R. |
Do I just put if else condition to handle non 200 responses?
|
Mark M. |
well, to be honest, I do not know
|
Dec 29 | 9:20 AM |
Mark M. |
again, I assumed that non-200 responses route to onFailure()
|
Ace R. |
okay. yes they do
|
Mark M. |
it is possible that Retrofit reserves onFailure() for other things (e.g., could not reach the server)
|
Mark M. |
unfortunately, the Retrofit documentation is a bit weak
|
Ace R. |
okay. yes I believe I have seen it route to onFailure
|
Ace R. |
so in the onFailure I can just display a Toast to the user
|
Mark M. |
I'd use something other than a Toast, but yes, you can let the user know about the problem
|
Ace R. |
other than a Toast? what other options would you use so that i can apply it in my app?
|
Mark M. |
well, a Toast is emphemeral -- if the user is not paying close attention to the screen, they might not see it
|
Ace R. |
Okay. maybe a SnackBar?
|
Mark M. |
whether you display the message somewhere inline in your main UI, or use a more durable overlay (e.g., a Snackbar) is up to you
|
Ace R. |
okay makes sense.
|
Mark M. |
let me take another question from Chandra, and I will be back with you in a bit
|
Mark M. |
Chandra: your turn! do you have another question?
|
Ace R. |
okay
|
Chandra S. |
I saw GsonConverterFactory. What's the function? Is it to change the converter from the default to Gson?
|
Mark M. |
are you referring to with Retrofit?
|
Chandra S. |
Yes
|
Dec 29 | 9:25 AM |
Mark M. |
with Retrofit 2, there is no default converter
|
Mark M. |
Retrofit 1.x assumed Gson as a converter with an option to replace it
|
Mark M. |
Retrofit 2.x says that you have to provide a converter
|
Mark M. |
Gson is a likely candidate, but there are others, particularly if the payload is not JSON
|
Chandra S. |
Ok, thanks, Mark
|
Chandra S. |
I actually have another question related to Retrofit
|
Mark M. |
since that one was pretty short, go right ahead
|
Chandra S. |
If REST API that I consumed has multiple child, let say like this format:
|
Chandra S. | |
Chandra S. |
I just want to retrieve node under "home", I should keep create class "Class" and "Person"?
|
Mark M. |
AFAIK, Gson does not know how to return arbitrary inner pieces of the JSON
|
Mark M. |
so I would assume that you need a Contacts class and a Person class, with just enough fields to get by
|
Mark M. |
note that you do not need fields for every JSON property, if you will not be using that property
|
Chandra S. |
Ok, got it.. Thanks
|
Mark M. |
now, it is conceivable that another converter, such as Jackson, offers other options here
|
Mark M. |
Gson and Moshi would require the full object tree to the objects that you want, AFAIK
|
Dec 29 | 9:30 AM |
Mark M. |
let me take another question from Ace, and I will return to you in a short while
|
Mark M. |
Ace: your turn! do you have another question?
|
Chandra S. |
Ok
|
Ace R. |
I read the chapter about UI Automator, the bit that I still don't quite understand is how is how different is it from Espresso? Can you elabrorate what you mean by UI automator has greater ability to test an applicaton vs testing individual component.
|
Mark M. |
Espresso only works within one app
|
Mark M. |
UI Automator works across apps
|
Mark M. |
so, for example, suppose you want to test your home screen launcher icon
|
Mark M. |
you have no means of doing that with Espresso, as you cannot automate the home screen with Espresso
|
Mark M. |
you can do that with UI Automator
|
Mark M. |
similarly, suppose you want to test your integration with some app that you launch from yours (e.g., you open up a PDF viewer app)
|
Mark M. |
you can launch an activity from Espresso and use mocks to determine if the Intent looked OK, but you cannot actually confirm if the activity that was started was what you wanted
|
Mark M. |
with UI Automator, you can
|
Ace R. |
okay. sounds quite powerful
|
Dec 29 | 9:35 AM |
Mark M. |
the downside is that since it relies upon the accessibility APIs, you have limited visibility into what you are testing
|
Mark M. |
testing more than "did this text show up where I expected it?" is difficult
|
Mark M. |
which is why UI Automator tends to be reserved for integration testing
|
Ace R. |
okay, in regards to Mock test do I need to use Robotium to start mockito testing or just add Mockito dependency in Gradle?
|
Mark M. |
Mockito and Robotium are independent
|
Mark M. |
you do not need one to use the other
|
Ace R. |
Okay what is Robotium use for?
|
Mark M. |
Mockito lets you mock arbitrary stuff, but you are responsible for setting up the mocks
|
Mark M. |
Robotium is effectively a canned set of mocks related Android SDK constructs
|
Ace R. |
So with Robotium you dont have to setup the Mocks?
|
Mark M. |
I'd phrase it more as "the setup of the mocks is easier"
|
Ace R. |
okay thanks.
|
Mark M. |
personally, I do not use either of them, outside of that one unit testing chapter in the book
|
Dec 29 | 9:40 AM |
Ace R. |
ohh
|
Ace R. |
thats interesting
|
Mark M. |
hence, I claim no deep expertise in either of them :-)
|
Ace R. |
can I ask do you use instead?
|
Mark M. |
Robotium is for unit testing (i.e., testing on the JVM, outside of Android)
|
Mark M. |
Mockito can be used for instrumentation testing (i.e., testing in Android) as well as unit testing
|
Mark M. |
but I tend to focus on testing real objects
|
Ace R. |
testing real objects? what if you have a Database though? I was wondering how you would handle that?
|
Mark M. |
if by "database" you mean SQLite on the device, I test the real database
|
Mark M. |
if by "database" you mean some sort of Web service, I have a test Web service, but real Android code for hitting it
|
Ace R. |
yes SQLite
|
Ace R. |
okay hmmn, if you did test the real database it would insert real data in the actual database?
|
Mark M. |
sure
|
Ace R. |
do you just delete the data after the test?
|
Mark M. |
yes
|
Mark M. |
or, perhaps before the next one
|
Mark M. |
I tend to use @Before more than @After
|
Ace R. |
okay hmmn but what if its a production app with already real data
|
Mark M. |
there are ~2 billion Android devices in the world, plus an infinite number of possible emulators
|
Mark M. |
surely one of them does not have production data
|
Ace R. |
ah!
|
Ace R. |
makes sense
|
Dec 29 | 9:45 AM |
Mark M. |
now, I'm somewhat unusual, in that I have a rather large fleet of hardware here in my Secret Mountain Lair
|
Mark M. |
so I do not claim that my approaches towards testing are necessarily apropos for everyone
|
Ace R. |
okay.
|
Mark M. |
so, having something to test on, that does not disturb any real data, is not an issue for me
|
Ace R. |
okay got it
|
Mark M. |
OK, it's free-for-all time
|
Mark M. |
if anyone has any questions, just chime in
|
Chandra S. |
What is the function of Call in Retrofit? Since I saw it's stated but never used
|
Mark M. |
sure it is used
|
Mark M. |
check my Retrofit 2.x samples in the book
|
Chandra S. |
In onResponse method I mean
|
Mark M. |
ah
|
Mark M. |
well, among other things, you can get to the full parsed response there
|
Mark M. |
such as HTTP headers
|
Mark M. |
oh, no, wait, I'm thinking of the Response object
|
Dec 29 | 9:50 AM |
Mark M. |
ah, you can get to the original Request object via the Call
|
Mark M. |
you can also call clone() to create a new Call (e.g., for retrying the request)
|
Mark M. |
(though that would be more likely in onFailure() than in onResponse())
|
Ace R. |
ah so clone will retry your original request if it fails for whatever reason?
|
Mark M. |
but the Response object will be used more in onResponse()
|
Mark M. |
it's more that given a fresh Call, you can execute() or enqueue() that new Call, to retry the request
|
Mark M. |
I don't think you can do that with a Call that has been processed already
|
Ace R. |
okay so in a Snackbar would you say its a good idea to call clone() if the user want to retry the request?
|
Chandra S. |
I see.. ok, thanks Mark
|
Mark M. |
Ace: if you mean from a Snackbar.Action (e.g., user clicks a "retry" button in the Snackbar), then that is a possibility
|
Mark M. |
however, when you display a UI to the user, there may be some time gap between the original Call and the time you decide to retry it
|
Mark M. |
in that case, I would tend to go back through my original code, as perhaps my call needs to change slightly, due to changes elsewhere in my app
|
Chandra S. |
I actually have last question :-D Do the setRetainInstance(true) has drawbacks like memory leaks? How Android manage the lifecycle of the fragment?
|
Mark M. |
OTOH, suppose you have set up your server to send a particular HTTP response code to indicate "we're busy, please retry"
|
Dec 29 | 9:55 AM |
Mark M. |