Jul 25 | 3:55 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Jul 25 | 4:00 PM |
Eric | has entered the room |
Mark M. |
hello, Eric!
|
Mark M. |
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.
|
Jul 25 | 4:05 PM |
Mark M. |
hopefully there is some unique identifier representing the download, such as the destination path
|
Mark M. |
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
|
Mark M. |
if you use ListAdapter, that will minimize any actual UI changes
|
Eric |
ok
|
Jul 25 | 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
|
Mark M. |
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
|
Mark M. |
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
|
Jul 25 | 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
|
Mark M. |
or, if you are using the Navigation component, it would be passed as a value through its arguments system
|
Mark M. |
the fragment would get that argument and use it to find out about changes to the particular download that it cares about
|
Mark M. |
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
|
Mark M. |
or uploads
|
Eric |
sorry let me summarize
|
Jul 25 | 4:25 PM |
Eric |
View paste
|
Mark M. |
OK, then, rather than "first fragment", I recommend that we use more concrete terms, like "list fragment" and "detail fragment" or something
|
Mark M. |
if the list fragment is showing results, then the list fragment does not care about an individual Uri
|
Mark M. |
it cares about updates to the collection of all jobs
|
Mark M. |
so it can render that collection in the list
|
Mark M. |
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?
|
Eric |
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
|
Jul 25 | 4:30 PM |
Mark M. |
the fragment then passes that List<> to a ListAdapter (subclass of RecyclerView.Adapter)
|
Mark M. |
ListAdapter then renders the list
|
Mark M. |
the viewmodel emits a new viewstate with an updated List<>, containing updated progress details, every so often
|
Mark M. |
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
|
Mark M. |
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."
|
Mark M. |
so, presumably, the activity is telling the shared viewmodel to start uploading that particular item
|
Jul 25 | 4:35 PM |
Mark M. |
(assuming it is the activity that has the onActivityResult() that you mentioned)
|
Mark M. |
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
|
Mark M. |
(BTW, hello sudokai -- I will be with you in a moment!)
|
Mark M. |
an activity-scoped viewmodel can be used both by the activity and by fragments that it hosts
|
Mark M. |
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
|
Jul 25 | 4:40 PM |
Mark M. |
I do not know enough about your app to answer that
|
Mark M. |
my guess is that yes, you will have a viewmodel that is shared by both fragments and, if needed, the activity
|
Mark M. |
let me take a question from sudokai, and I will try to get back to you before the end of the chat
|
Mark M. |
sudokai: hi! how can I help you today?
|
Eric |
ok
|
sudokai |
Hi Mark
|
sudokai |
I just wanted to know if you have some specific pointers on Android app performance
|
sudokai |
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
|
sudokai |
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
|
Mark M. |
that implies that you are doing far too much work on the main application thread
|
Mark M. |
for ANR-caliber problems, consider configuring StrictMode to complain more (e.g., about file I/O)
|
Mark M. |
often, the 5-10 seconds to trigger an ANR means doing I/O on the main application thread
|
Jul 25 | 4:45 PM |
sudokai |
So, most performance problems are because of work on the main app thread?
|
Mark M. |
not exactly
|
Mark M. |
there are lots of performance problems
|
Mark M. |
problems related to dropped frames probably are tied to too much work on the main application thread
|
Mark M. |
ANRs, by definition, are from too much work on the main application thread
|
sudokai |
I'm seeing issues transitioning into a new screen
|
sudokai |
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
|
sudokai |
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 ?
|
sudokai |
To diagnose this stuff
|
sudokai |
StrictMode okay
|
Mark M. |
Android Studio has the Profiler tool
|
sudokai |
I'll check that
|
sudokai |
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
|
Jul 25 | 4:50 PM |
sudokai |
I also have Stuck partial wake locks
|
Mark M. |
I don't really know what "stuck" means in this context
|
Mark M. |
my point is that an ANR should be glaringly obvious
|
sudokai |
It's from Google Play Console
|
sudokai |
"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
|
sudokai |
No more questions from me
|
Mark M. |
OK
|
Mark M. |
Eric: back to you! do you have another question?
|
Eric |
View paste
|
Jul 25 | 4:55 PM |
Mark M. |
your list fragment does not have a list of uploads, though -- it has just one
|
Jul 25 | 4:55 PM |
Eric |
then why is it called a list fragment?
|
Mark M. |
I can only go by what you write
|
Mark M. |
you wrote: "If I upload something it is added to a recyclerview showing the download progress"
|
Mark M. |
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
|
Mark M. |
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.
|
Mark M. |
the viewmodel emits new List<RowStuff> as progresses change
|
Eric | has left the room |
Mark M. |
ah, well
|
Mark M. |
sudokai: any quick last question?
|
sudokai |
No that's all
|
sudokai |
Thank you
|
Jul 25 | 5:00 PM |
Mark M. |
you're welcome!
|
Mark M. |
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 |