Office Hours — Today, July 25

Thursday, July 23

Jul 25
3:55 PM
Mark M.
has entered the room
Mark M.
turned on guest access
4:00 PM
Eric
has entered the room
Mark M.
hello, Eric!
how can I help you today?
Eric
hello. I would like to create a download manager UI. If I upload something it is added to a recyclerview showing the download progress. The function I am given is an Observable returning the current progress of a download. My problem is knowing which row to update whenever the observable reports back a progress.
4:05 PM
Mark M.
hopefully there is some unique identifier representing the download, such as the destination path
if so, a viewmodel that is observing the Observable could emit an updated viewstate where that row's model data reflects the updated progress amount
Eric
good idea, that should work
Mark M.
your activity/fragment that observes those viewstates (e.g., via LiveData) can then update the RecyclerView
if you use ListAdapter, that will minimize any actual UI changes
Eric
ok
4:15 PM
Eric
if I wanted to click on a row to show a detail view that also displays the progress, would you recommend architecting it by placing the upload logic into an activity and toggling visibility of 2 fragments? The first fragment contains the list of progress bars for different jobs. The second fragment contains details of the selected job but also contains the progress bar.
Mark M.
yeah, I'm definitely steering people towards fewer activities and more fragments
for example, the viewmodel might be shared between the fragments, so they can each observe the updates
Eric
The way I had it figure the activity had a button to select a file uri. Am I supposed to pass this uri to a fragment's viewmodel?
Mark M.
that would depend a bit on how you are setting up the fragments and switching between them
one pattern says that the detail fragment is transient: it exists while it is on the screen and goes away afterwards when the user performs back navigation
4:20 PM
Mark M.
in that case, your identifier could go in an argument (so it gets retained across configuration changes) when you create the fragment
or, if you are using the Navigation component, it would be passed as a value through its arguments system
the fragment would get that argument and use it to find out about changes to the particular download that it cares about
if, on the other hand, you were planning on the detail fragment being durable and existing continuously (say, in a master-detail pattern on larger screens), then probably I would have the communications go through the shared viewmodel
Eric
in the first case, right after I get the file uri do I show my first fragment, passing the file uri through an argument?
Mark M.
sorry, I thought the first fragment was the list fragment
Eric
my requirement is just that the progress should continue once the upload starts
Mark M.
yes, but if I understood correctly, you had a collection of downloads
or uploads
Eric
sorry let me summarize
4:25 PM
Eric
View paste
1.select something in activity.  onActivityResult has file uri.
2.somewhere I use this file uri to call upload which returns a stream of integers describing progress of an upload.
3.maybe my activity contains an embedded fragment that is a list of download jobs(showing progress of different downloads in progress)
4.clicking a row takes me to details of this download, and shows me the same progressbar ie when I switch back and forth I always see my progress updated
Mark M.
OK, then, rather than "first fragment", I recommend that we use more concrete terms, like "list fragment" and "detail fragment" or something
if the list fragment is showing results, then the list fragment does not care about an individual Uri
it cares about updates to the collection of all jobs
so it can render that collection in the list
the detail fragment cares about whatever list item the user clicks on in the list fragment, and how you choose to identify that is up to you -- it could be by the Uri if you wanted
Eric
how does the list fragment know which row to update with the numbers it is getting?
for example, I upload 1 picture and 1 row is added to list fragment. I upload a second picture. Picture one should show 10% and Picture two should show 0%
Mark M.
my recommendation was that the viewmodel supporting this fragment emits a viewstate containing a List<> of objects representing the jobs
4:30 PM
Mark M.
the fragment then passes that List<> to a ListAdapter (subclass of RecyclerView.Adapter)
ListAdapter then renders the list
the viewmodel emits a new viewstate with an updated List<>, containing updated progress details, every so often
ListAdapter, through the "differ" object you configure it with, will determine what rows in the RecyclerView need to be updated based on the data changes
so your fragment, directly, does not care which particular row needs changing -- it hands the updated List<> of model data to the ListAdapter and lets ListAdapter do its thing
Eric
who is calling upload(uri)?
Mark M.
you wrote "select something in activity. onActivityResult has file uri."
so, presumably, the activity is telling the shared viewmodel to start uploading that particular item
4:35 PM
Mark M.
(assuming it is the activity that has the onActivityResult() that you mentioned)
whether the viewmodel itself is doing the upload, or whether it delegates that work to a repository, is up to you and depends a lot on whether this work should get canceled if the user navigates away from this activity
Eric
ok so the activity does the uploading. The activity knows the fileUri and the progress. How does it get communicated to the viewmodel you are talking about? Is this list fragment viewmodel?
Mark M.
if the activity is the one that is telling the viewmodel to upload something, then I am imagining that this would be a shared viewmodel that both the activity and at least the list fragment would use
sudokai
has entered the room
Mark M.
and the activity probably does not know about the progress, unless I am misunderstanding things
Eric
that is what I am trying to understand based on the explanation I dont know if it is a viewmodel for the activity or list fragment now
Mark M.
it is one for both
(BTW, hello sudokai -- I will be with you in a moment!)
an activity-scoped viewmodel can be used both by the activity and by fragments that it hosts
such a viewmodel gets used for things that need to be shared between the fragments in that activity, and in this case by the activity itself
Eric
do we have a viewmodel shared by everything? The list needs to know all progresses, the detail needs to know one progress. I only mentioned the activity calling upload because that is the only way i understood 2 fragments being in sync about the progress
4:40 PM
Mark M.
I do not know enough about your app to answer that
my guess is that yes, you will have a viewmodel that is shared by both fragments and, if needed, the activity
let me take a question from sudokai, and I will try to get back to you before the end of the chat
sudokai: hi! how can I help you today?
Eric
ok
sudokai
Hi Mark
I just wanted to know if you have some specific pointers on Android app performance
I'm seeing a lot of frozen frames in metrics
Mark M.
do you mean dropped frames? where the UI is frozen?
sudokai
Yeah, I suppose so
That's what I see in Firebase Performance
Mark M.
I don't use that, so I cannot comment on it
sudokai
On Google Play Console, I'm seeing ANRs as well
Mark M.
that's not good
that implies that you are doing far too much work on the main application thread
for ANR-caliber problems, consider configuring StrictMode to complain more (e.g., about file I/O)
often, the 5-10 seconds to trigger an ANR means doing I/O on the main application thread
4:45 PM
sudokai
So, most performance problems are because of work on the main app thread?
Mark M.
not exactly
there are lots of performance problems
problems related to dropped frames probably are tied to too much work on the main application thread
ANRs, by definition, are from too much work on the main application thread
sudokai
I'm seeing issues transitioning into a new screen
Like, it's not smooth
Mark M.
that is a visual indication of dropped frames
sudokai
I'm already loading data via RxJava IO threads in a viewmodel
So not sure how I can improve that
Mark M.
for dropped frames alone, the problem could be doing too much work in general -- mobile CPUs are not nearly as powerful as notebook/desktop/server counterparts
sudokai
I thought data loading was the issue, but maybe it's somewhere else?
Mark M.
again, I recommend ensuring that StrictMode is configured to crash in a debug build, so that you are sure that I/O is not a problem
sudokai
So do you know any tools ?
To diagnose this stuff
StrictMode okay
Mark M.
Android Studio has the Profiler tool
sudokai
I'll check that
Okay thanks
Mark M.
ANRs, though, are massive
sudokai
That's all for me
Mark M.
dropped frames means that you maybe tied up the main application thread for 100ms or so
sudokai
Yes, I have lots of those
Mark M.
ANRs mean that you tied up the main application thread for 5000-10000ms
4:50 PM
sudokai
I also have Stuck partial wake locks
Mark M.
I don't really know what "stuck" means in this context
my point is that an ANR should be glaringly obvious
sudokai
It's from Google Play Console
"Stuck partial wake locks" 0.32%
Mark M.
off the top of my head, I am uncertain how they define "stuck", sorry
sudokai
Okay thanks
No more questions from me
Mark M.
OK
Eric: back to you! do you have another question?
Eric
View paste
based on what we discussed I think this could work:

1.once I get a file uri, create the list fragment and pass it in.
2.the list fragment creates an UploadViewModel that calls upload(uri).
3.whenever a progress is returned, call notifyItemChanged(pos).  I get pos by looking for the index containing the current uri.
4.when a row is selected, I create a detailfragment.  The detailfragment also uses this viewmodel to listen for changes in progress.
4:55 PM
Mark M.
your list fragment does not have a list of uploads, though -- it has just one
4:55 PM
Eric
then why is it called a list fragment?
Mark M.
I can only go by what you write
you wrote: "If I upload something it is added to a recyclerview showing the download progress"
and you wrote: "The first fragment contains the list of progress bars for different jobs. The second fragment contains details of the selected job but also contains the progress bar."
Eric
do you understand what I am trying to accomplish?
Mark M.
your explanation has been shifting, so probably I do not
Eric
it was just a suggestion; I wasn't sure if I was going on the correct path
Mark M.
and we are running out of chat time, so at this point, I do not think we can really start over
Eric
basically I want a list of progressbars. if i click a progressbar it should also updated
Mark M.
in general, I stand by my recommendations from earlier
two fragments, probably with a shared viewmodel
Eric
bye
Mark M.
the viewmodel has a LiveData<List<RowStuff>>, where RowStuff contains whatever you want in your list: name, progress %, etc.
the viewmodel emits new List<RowStuff> as progresses change
Eric
has left the room
Mark M.
ah, well
sudokai: any quick last question?
sudokai
No that's all
Thank you
5:00 PM
Mark M.
you're welcome!
the transcript for this chat will go up on https://commonsware.com/office-hours/ shortly
sudokai
See you around Mark
Mark M.
see you later! have a pleasant day!
sudokai
has left the room
Mark M.
turned off guest access

Thursday, July 23

 

Office Hours

People in this transcript

  • Eric
  • Mark Murphy
  • sudokai