Office Hours — Today, December 15

Saturday, December 12

Dec 15
3:50 PM
Mark M.
has entered the room
Mark M.
turned on guest access
3:55 PM
Susheel
has entered the room
Mark M.
hello, Susheel!
Susheel
Hi Mark how are you?
Mark M.
how can I help you today?
Susheel
I have a question about GridLayoutManager ...
4:00 PM
Mark M.
go ahead
Susheel
So I have a recylerview that is using GridLayoutManager and the spancount is set to 3....In each individual item, there is an imageview with width as match_parent and height as wrap_content....depending on the device width, the imageitem's dimensions also change
Steve
has entered the room
Steve
Hi
Mark M.
(BTW, hi, Steve, I'll be with you shortly!)
Steve
Thanks, Mark.
Susheel
Now when I launch the app on my device, I am seeing the initial few rows with the images loaded but when I scroll down, the items look like they are being drawn at runtime...and causing a bad UI experience
Mark M.
well, the items *are* being drawn at runtime, as were your first few rows' worth
Susheel
right
Mark M.
what specifically do you mean by "causing a bad UI experience"?
does "being drawn at runtime" mean "slow"?
Susheel
I mean, the height of each row is changing as if from zero to the wrap_content...
Mark M.
when are the images being put in the rows? directly, or asynchronously?
Susheel
using picasso in the onbindviewholder method of my recylerviewadapter
Mark M.
are you using a placeholder drawable with Picasso?
Susheel
yes
Mark M.
hmmm... I was expecting a "no" there... :-)
Susheel
but that does not affect my issue though
4:05 PM
Mark M.
so, if "the height of each row is changing as if from zero to the wrap_content", is that happening as the row is showing the placeholder, or is that as the row is swapping in the real image?
Susheel
when I hardcode the imageview dimensions I don't see this problem but my spancount has to be 3 so I cannot hardcode the image item's width
That is happening as the row is showing
I have this issue even without the placeholder image set
Mark M.
that didn't answer my question
that's fine, but that still did not answer my question
Susheel
oops
Mark M.
you say the height of the row is changing
that implies the passage of time
you start in one state (short or zero height)
you end in another state (expected height) after some amount of time
I am trying to understand whether that amount of time is tied into the asynchronous loading of the real image or not
Susheel
the height of the row is same for a given device, the height of the image item is changing it seems like...
Mark M.
when "the height of the image item" is done "changing", are you seeing the placeholder, or the final image?
also, are all images (placeholder plus the real ones) the same size?
Susheel
You know what, I am actually not using a placeholder image on this adapter....sorry about that...I just checked and I am not using a placeholder image...
Mark M.
you might try adding one
Susheel
oh
4:10 PM
Mark M.
the ImageView height will be zero if there is no image in it
4:10 PM
Susheel
but will it look consistent on different devices?
right
just using mdpi, hdpi folders I guess?
Mark M.
typically, yes
Susheel
thank you I will try that
Mark M.
let me take a question from Steve, and I'll be back with you in a bit
Susheel
cool
Mark M.
Steve: sorry for the delay! how can I help you today?
Steve
Thanks, Mark. I have a very basic question about using a Service from a Fragment. I'll try to paste in my question.
View paste
I have a ListFragment that uses an IntentService to make a network request and a local
BroadcastReceiver to get results back. I'm wondering if I'm using the fragment's lifecycle
events properly:

1. I start the service in onActivityCreated().
2. I register the receiver in onResume().
3. I unregister the receiver in onPause().
Mark M.
the register and unregister seem OK
Steve
Ok.
Mark M.
in terms of when to start the service, that's tough to answer in the abstract
Steve
Ok. What further details would help?
Mark M.
what are you doing about configuration changes (e.g., screen rotations)?
onActivityCreated() gets called again after a configuration change IIRC
onCreate() would not be
Steve
That's something I can use some help with. What would you suggest in terms of handling configuration changes?
Mark M.
again, that's tough to answer in the abstract
Chandra S.
has entered the room
Mark M.
let's back up a step: why is this an IntentService, versus just a plain thread?
(BTW, hello, Chandra -- I will be with you shortly!)
Rajeef
has entered the room
Mark M.
(BTW, hello, Rajeef -- I will be with you in a bit, after Steve and Chandra!)
4:15 PM
Mark M.
Steve: is this work going to continue even after the activity and fragment are destroyed?
Rajeef
No problem
Chandra S.
OK Mark
Mark M.
Steve: usually, with an IntentService, the idea is that the work might take a while, and you want to be fairly decoupled from the UI layer as a result
Steve
Yes. I'd like to download the information from the network and store it for later use. I don't need to keep downloading it each time the activity/fragment start up.
Mark M.
in that case, you probably only want to call startService() once
and in that case, I would lean towards onCreate() rather than onActivityCreated()
well, actually, it's probably a wash
Steve
Ok. The service makes a network call to get a list of contacts, and the contacts are displayed in a list view. Will that work in onCreate?
Mark M.
both will get called if the fragment is destroyed and recreated
the IntentService neither knows nor cares whether you are calling it from onCreate(), onCreateView(), onActivityCreated(), onViewCreated(), or anything else
Steve
I understand that. But how do I control when I get the results back so I can display them?
Is that by registering the receiver?
Susheel
has left the room
Mark M.
well, you don't really "control when [you] get the results back"
that will be some time after you call startService(), and the exact amount of time is indeterminate
4:20 PM
Mark M.
ideally, you register the receiver ahead of, or contemporaneously with, calling startService(), in the off chance the service gets done before your receiver is ready
Steve
Ok. Given that the receiver does things with the UI, don't I have to wait for the UI to be created before registering the receiver?
Mark M.
you'll also need to take into account the possibility that the service completes before your ListView is ready, particularly if the IntentService says "oh, hey, I have this stuff cached already, I'm done!"
Steve
That's what I'm asking now: how do I avoid getting the results back before the ListView is ready?
Mark M.
if the UI is not ready, the receiver needs to cache the result, so you can apply it later on
the safest thing is to not call startService() until onViewCreated() or something
Steve
Great. I'll try that.
Mark M.
and decide how you want to handle configuration changes, so you don't call the service twice unnecessarily (e.g., retained fragment and a boolean isTheServiceStarted flag or something)
Steve
Should I register the receiver in onViewCreated as well, or still do that in inResume?
Mark M.
I'd do it in onResume(), personally
don't register it twice
if you run into race conditions where onResume() is too late, then perhaps register it in onViewCreated(), but keep track of whether it is registered and therefore should be skipped in onResume()
there are a lot of moving parts here, and that's why it's a bit difficult to really give definitive answers in the abstract
let me take questions from the others, and I'll be back with you in a bit
Chandra: your turn! do you have a question?
Steve
Sure, thank you.
Chandra S.
I have an activity with a ViewPager, a TabLayout, and 3 fragments inside that.
When going from fragment 1 -> fragment 2 -> fragment 1, the fragment 1 is not calling onCreate & onCreateView, but when going from fragment 1 -> fragment 2 -> fragment 3 -> fragment 2 -> fragment 1, it's calling a onCreateView only, without onCreate.
I saw on the fragment lifecycle docs that it's somehow different, can you clarify?
Mark M.
what PagerAdapter subclass are you using?
4:25 PM
Chandra S.
FragmentStatic
Mark M.
sorry, but I do not recognize that class
Chandra S.
Wait, I'm checking
Mark M.
I was expecting FragmentPagerAdapter or FragmentStatePagerAdapter
or your own custom PagerAdapter-from-scratch implementation, perhaps
Chandra S.
Ah yes the FragmentState
Mark M.
FragmentStatePagerAdapter will release the views associated with fragments that are "far" off screen
that's to save on heap space, so those views can get garbage-collected
when you swipe back towards that fragment, FragmentStatePagerAdapter needs to have the fragment re-create the widgets
Chandra S.
So if I have only 3 tabs, better to have FragmentPagerAdapter?
Mark M.
yes
Chandra S.
Is that the only difference?
Mark M.
FragmentStatePagerAdapter is for when you have lots of pages
it's certainly the primary difference
Chandra S.
But why it's calling only the onCreateView not both of onCreate and onCreateView?
Mark M.
because the fragment is still created and not yet destroyed, presumably
4:30 PM
Mark M.
I have not looked at the implementation of FragmentStatePagerAdapter in quite some time, so I do not recall all the details
let me take a question from Rajeef, and I will be back with you in a bit
Rajeef: sorry about the delay! how can I help you today?
Chandra S.
Sure Mark thanks
Rajeef
Hi Mark, Thanks for your time.
View paste

I'm trying to implement infinite scrolling for a recycler view. 
Here's where I've taken inspiration for writing my custom scroll listener- https://gist.github.com/rogerhu/17aca6ad4dbdb3fa5892#file-endlessrecyclerviewscrolllistener-java 

The API I'm hitting gives back response in the form of 5 objects, every time offset is changed. 

I'm struggling with setting the Threshold value, especially for accomodating cases such as total count of response being less than 5.
Mark M.
um, I don't know that I'm going to be able to help you here
can you give me a more specific question?
Rajeef
Ok. In that case, do you have any suggestions/resources for implementing infinite scrolling with recyclervie ?
*recyclerview
Mark M.
I'd look for a library for it on the Android Arsenal
well, rephrase that, I'd avoid infinite scrolling
Rajeef
Oh Why's that ?
Mark M.
IMHO, outside of Twitter timeline-like scenarios, it's a failed UI pattern
Rajeef
View paste
Aah.Ok. So the use case here is to implement pagination. And the solution that came to my mind was infinte scrolling.
4:35 PM
Mark M.
do something else that allows the user to get to the right content, rather than expecting them to scroll and scroll and scroll and scroll and scroll and scroll and scroll and...
Rajeef
How else do you suggest pagination can be implemented
?
Mark M.
by not implementing pagination
if the content is that huge, focus on search, or browsing by category, or something
all that being said, if I had to implement infinite scrolling, I'd look for a library for it on the Android Arsenal
Rajeef
Uhmm.. the Api is implemented in such a way. There are close to 700-1000 records. And its expects an offset value every time a new network call is made :/
Aah. Ok. Library it is then.
Thanks Mark.
Mark M.
if the solution is "force the user to scroll through 1000 entries", then you have more fundamental problems
let me take questions from others, and I'll be back with you in a bit if there's time
Rajeef
Yes Indeed.
Mark M.
Steve: back to you! do you have another question?
Rajeef
Sure.
Steve
I do have a follow-up question on what I was asking about earlier.
Mark M.
go right ahead
Steve
View paste
The main screen of the app displays a list of contacts. The list of contacts is returned
from the entwork. When the user adds or deletes a contact, the list needs to be updated
from the network, but that's the only time.
I'm wondering what you would recommend in terms of a design for this scenario.
Mark M.
um, a design for what?
Steve
Is it reasonable to use an IntentService in this case?
4:40 PM
Mark M.
you mean to do the network I/O? sure
I assume that "user adds or deletes a contact" is referring to doing this from your app's UI
Steve
Yes.
Mark M.
in that case, when the user adds/deletes a contact, you update your local data store (if relevant), then do the network I/O
all that could be done in the IntentService
Steve
Ok, great.
Mark M.
where the IntentService uses an event bus (you seem to be using LocalBroadcastManager) to let the UI layer know about results, if the UI layer is still around
perfectly reasonable
Steve
Great. Then regarding when to call the service from the fragment, I should look back at our discussion earlier today?
Mark M.
for add/delete, you call the service when the user adds or deletes
that part should be very straightforward
Steve
Right. But I'm wondering about displaying the list of contacts prior to that.
Mark M.
yes, that's what we talked about earlier in the chat
Steve
Great, thanks. I'll review that.
Mark M.
BTW, the chat transcript will be published at https://commonsware.com/office-hours/ shortly after the chat ends
let me take questions from the others, and I'll be back with you in a bit if there's time
Steve
Thank you. I'll plan on getting that.
Mark M.
Chandra: your turn! do you have another question?
Chandra S.
Yes, let say I'm creating a chat app.
4:45 PM
Chandra S.
Is there any way to have like an 'onInternetReconnected' that will send all pending network related task while when the user still disconnected they will have the message they typed saved in local.
4:45 PM
Mark M.
I'm creating a chat app
Chandra S.
Or should we check regularly with service?
Ah which chat app, Mark?
Mark M.
sorry, that was a joke
you said "let's say I'm creating a chat app"
Chandra S.
Hahaha OK
Mark M.
so I followed the instructions
:-)
Rajeef
:)
Mark M.
anyway, you can monitor connectivity-change broadcasts (see ConnectivityManager), and kick off an IntentService to flush any outstanding work from a queue
usually, when I think of a chat app, though, a loss of connectivity doesn't queue up messages
otherwise, a lot of stale messages might get posted hours later
but, for other scenarios (e.g., Steve's add/delete contacts), if there's no connectivity now, you can watch for connectivity-change broadcasts and try to do the network I/O then
on Android 5.0+, JobScheduler can simplify this a bit, but that's only Android 5.0+
Chandra S.
And is there any way to detect the GPS coming from the real one, not from the apps like FakeGPS?
Mark M.
off the top of my head, I don't know
I haven't looked into that ever
sorry
let me take questions from the others, and I'll swing back to you if I can for any follow
Chandra S.
OK Mark thanks
4:50 PM
Mark M.
er, any follow-up
Rajeef: back to you! do you have another question?
Rajeef
Nope. That's all for today.Thanks.
Mark M.
OK
Steve: do you have another question?
Vasanthi
has entered the room
Steve
I do have another question:
View paste
I am trying to work out a design to meet the following requirement:

When the app starts, it needs to start polling for Bluetooth input,
and continue polling as long as the app runs (across different activities).

Here is the design I'm thinking of:
1. Use an IntentService to handle reading the Bluetooth messages.
2. In the IntentService, use a LinkedBlockingQueue to manage worker threads
to do the reading.
3. In the IntentService, use an endless for loop to add Runnables to
the LinkedBlockingQueue. Each Runnable blocks until it reads one message.
4. Use a LocalBroadcastManager to transmit received messages to activities,
as appropriate.

Is this reasonable?
Vasanthi
Hi Everyone
Mark M.
(BTW, Vasanthi, the chat is nearly ending, but I'll try to take a question from you in a moment)
Steve: replace "IntentService" with "Service" and you'll at least be closer
Vasanthi
Oh, sorry, I thought it will be for another 40min
Chandra S.
A very good session, thanks everyone, see you
Rajeef
has left the room
Chandra S.
has left the room
Mark M.
IntentService destroys itself after onHandleIntent() returns
regarding the Bluetooth bits, I've never used them, so I don't know how practical what you're describing is, though it seems reasonable
Steve
Ok. Are there examples in your book that I you would suggest I look at?
For the Service.
Mark M.
well, they're a bit overkill, but the media projection API samples have services that are closer to what you're doing here than the rest of my samples
Steve
Thank you, Mark. I will look at them.
Mark M.
OK
Vansanthi: your turn! do you have a quick question?
Vasanthi
View paste
I am writing an app with TreeView, with the data taken from database at the moment. I have a base adapter implementing ListAdapter and a mainAdapter extending the base adapter. 
For unit testing these custom adapters, 
1)	Should I test the base adapter and the main adapter individually or do I only test the mainAdapter? 
2)	Which class should my unit test case derive from? Should it be AndroidTestCase or something else? 
3)	I assume for the unit testing of the adapter, I can skip the database, but use some static data. Am I right?
4)	Is it enough if I test the public methods in the adapter class(s)?
4:55 PM
Mark M.
I can't really answer 1, 3, and 4, as I don't know your app at all
with regards to 2, AndroidTestCase is for the old JUnit3 instrumentation tests
you're probably wanting to use JUnit4 instrumentation tests or possibly true unit tests
in either case, you don't inherit from any particular class
Vasanthi
So, for JUnit4, which class should I derive frm
Mark M.
tests are handled via annotations with JUnit4
so, you can derive from Object for all the testing stuff cares
Vasanthi
OK, so I don't need to use any of the Android specific test classes?
Mark M.
you might create your own base class for common functionality across your tests, and inherit from that, but that's purely your decision
correct
Vasanthi
Thank you - regarding question 1), my app uses the mainAdapter, which is derived from the baseAdapter. For the unit test, do I need to test both adapters separately?
Mark M.
I can't really answer that
you need to test enough that you think it workse
er, works
if you're aiming for 100% code coverage, presumably you need to test both adapters
whether they can be tested 'separately' depends on a lot on their implentation
Vasanthi
OK. Thank you very much. I will try with JUnit4 and if I have any problems, will attend your tomorrow's session. Thanks a lot
Mark M.
sorry I couldn't be more useful
Steve
Thank you for the session, Mark. Are the chats always one hour long?
Vasanthi
Could you please let me know about the Android training?
Mark M.
almost always
Vasanthi: I am not certain what you mean, exactly
I offer private training to organizations: https://commonsware.com/training/
5:00 PM
Mark M.
if you have questions about that, email me at training@commonsware.com
Vasanthi
Thank you very much. Will mail you. Bye
Mark M.
that's a wrap for today's chat
the transcript will be posted on https://commonsware.com/office-hours/ shortly
Steve
has left the room
Vasanthi
has left the room
Mark M.
the next chat is tomorrow at 7:30pm US Eastern
Mark M.
turned off guest access

Saturday, December 12

 

Office Hours

People in this transcript

  • Chandra Siahaan
  • Mark Murphy
  • Rajeef
  • Steve
  • Susheel
  • Vasanthi