Nov 3 | 3:50 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Nov 3 | 4:00 PM |
Aaron | has entered the room |
Mark M. |
hello, Aaron!
|
Mark M. |
how can I help you today?
|
Aaron |
hey, hey. I am ready for that mini-code review if it works out today :)
|
Mark M. |
OK
|
Aaron |
here's what I came up with in terms of priorities
|
Aaron |
View paste
(2 more lines)
|
Mark M. |
OK
|
Nov 3 | 4:05 PM |
Mark M. |
particularly in a chat structure, I'm going to have to do this pretty much "stream of consciousness", as I read things
|
Aaron |
my general game plan is to make changes based on your feedback, publish the first version to Google Play and put it in front of a small group of people who will be interested while I integrate RxJava and convert some classes to Kotlin and write some unit tests. FYI
|
Aaron |
yep, sounds good
|
Mark M. |
so while I can take the classes at least somewhat in the order that you list them, my comments will likely be all over the map (high level, low level, etc.)
|
Aaron |
yep
|
Mark M. |
I'll try to budget time at the end to circle back and try to address some of your architecture-y concerns
|
Aaron |
that would be awesome
|
Mark M. |
so, first nit: NoSurfRepository.java has an empty case FETCH_SUBSCRIBED_POSTS_SYNC that you could remove or add a // TODO comment to indicate what your plans are
|
Mark M. |
your onFailure() callbacks just seem to log to LogCat -- how does the user find out if something doesn't work?
|
Nov 3 | 4:10 PM |
Mark M. |
question: for your methods whose names end in "Sync" -- what does the "Sync" mean?
|
Aaron |
asynchronous network calls
|
Mark M. |
then... shouldn't it be "Async" instead of "Sync"?
|
Aaron |
that was a convention mentioned in a course I took, I thought the same thing. guess it's not common, I'll change it
|
Mark M. |
having a convention to indicate an asynchronous call is reasonable... but then the convention should indicate an asynchronous call
|
Aaron |
agreed
|
Mark M. |
IMHO, "Sync" implies a synchronous call, or possibly data synchronizatino
|
Mark M. |
er, synchronization
|
Mark M. |
but on the whole, I don't see method names using that sort of suffix all that often
|
Aaron |
ok, good to know
|
Aaron |
oh yeah, one other thing I'll mention in terms of my game plan, is that instead of starting another app after this or trying to add new features, I'm going to spend a lot of time reading other people's code from big open source projects. think I can learn a lot that way
|
Mark M. |
warning: just because it's open source does not mean that it either good or all that readable
|
Mark M. |
code smell: don't use an AsyncTask when a Thread will do
|
Nov 3 | 4:15 PM |
Mark M. |
in particular, if your AsyncTask does not override onPostExecute(), because you have nothing that you need to do on the main application thread, do not use AsyncTask
|
Aaron |
OK
|
Mark M. |
probably use a ThreadPoolExecutor over a plain Thread, but either of those over an AsyncTask where one is not needed
|
Aaron |
makes sense, as I understand AsyncTask is there to help post results to the UI thread
|
Mark M. |
right, and you're not really using it for that
|
Mark M. |
AsyncTask also has a bad reputation nowadays, and so using it is "not a good look", as they say
|
Mark M. |
in your viewmodel, while I used "stitched together" as a phrase, I suspect that using "stitched" in a field name is going to cause confusion
|
Aaron |
because it has another common meaning, or because it has no common meaning?
|
Mark M. |
because it's mostly used in sewing :-)
|
Aaron |
I called it zip at first but I felt like that would be confusing since it's not really that
|
Aaron |
LOL
|
Aaron |
ok
|
Nov 3 | 4:20 PM |
Mark M. |
"merged" or "combined" might be better choices
|
Nov 3 | 4:20 PM |
Mark M. |
BTW, overall: load up on code comments
|
Aaron |
OK
|
Mark M. |
your objective is to impress code reviewers more than end users, if I understand your situation correctly
|
Mark M. |
reviewers coming in cold to a code base appreciate comments
|
Aaron |
both, but yes, and will do
|
Mark M. |
you have some good tactical ones, but JavaDoc comments would be a nice addition
|
Aaron |
roger that
|
Mark M. |
question: in your viewmodel, why do you have a separate initApp() method, instead of initializing in the constructor?
|
Mark M. |
IOW, what is the trigger for initApp() being called, if not just when the viewmodel is instantiated?
|
Aaron |
that was one of the first methods I wrote, and I think at the time it was important that it happen at some specific point when I was calling it from the activity's onCreate, but I don't think it matters anymore, can probably roll it into the constructor
|
Aaron |
good call, I'll check it
|
Nov 3 | 4:25 PM |
Mark M. |
question: if logUserOut() is called on the viewmodel, is the viewmodel still usable? or will other viewmodel methods start to fail until something is done to log the user in again?
|
Aaron |
mostly it is, only the methods with "Subscribed" in the name rely on the user being logged in. all the others can work with an anonymous token
|
Aaron |
and those methods will do nothing if the user is logged out
|
Mark M. |
at minimum, that's worth a comment
|
Aaron |
OK
|
Aaron |
it would be more obvious to someone familiar with Reddit, but you are right, I should not assume that anyone looking at the code is familiar.
|
Aaron |
*everyone
|
Nov 3 | 4:30 PM |
Mark M. |
I would review how you are using SingleLiveEvent, as I don't think you're using it correctl
|
Mark M. |
er, correctly
|
Mark M. |
in particular, I don't think that the way it is written that you're supposed to do your "consume" step
|
Mark M. |
personally, I prefer the "Use an Event wrapper" approach from https://medium.com/androiddevelopers/livedata-w...
|
Aaron |
OK yes I think you are right, and I will investigate that
|
Mark M. |
another area that could really use comments is anywhere you find yourself having to work around oddities in somebody else's stuff
|
Mark M. |
for example, if you find yourself naming a parameter twiceEncodedSelfTextHtml, you probably need some comments
|
Aaron |
OK agree
|
Nov 3 | 4:35 PM |
Mark M. |
your repository is exposing Retrofit data transfer objects (DTOs) as its model data -- as I think we discussed in a previous chat, ideally your repository exposes model objects that are designed for your UI consumption, hiding the DTOs
|
Aaron |
oh I see. so you are saying, the "stitch" method and all that should be in the repository instead of the vm?
|
Mark M. |
yes
|
Mark M. |
that way, if Reddit changes details of their API, or you switch to something other than Retrofit, or you start caching data, the UI doesn't care
|
Mark M. |
hide all the icky stuff in the repository
|
Mark M. |
the UI is plenty icky already just due to Android concerns :-)
|
Aaron |
I see, I had put it in the vm on the premise that one role of the vm is to prepare data for the UI, so that seemed like the logical thing to do
|
Aaron |
I guess I have the wrong definition of "prepare" in this case
|
Mark M. |
you can think of it as being three tiers:
|
Mark M. |
on the lowest level, you have the data that you get back from Retrofit and Room
|
Mark M. |
those have limitations based on Retrofit, Gson, Room, etc. and may not be ideal for use in the UI layer
|
Mark M. |
in the middle level, you have model objects that clean up those Retrofit/Room objects into something that is more natural
|
Mark M. |
possibly, at the high level, you have objects that represent the subset of the models that you need, with transformations that might be needed in a specific UI element
|
Nov 3 | 4:40 PM |
Mark M. |
(that may or may not be needed depending on the complexity of the app)
|
Mark M. |
so, for example, let's take the classic problem: dates
|
Mark M. |
your Web service will represent dates on way, and your Room entities might represent dates some other way
|
Mark M. |
so the lower-level objects (DTOs) need to know about that
|
Mark M. |
the "true" model objects in the middle layer would use something more natural: java.util.Date, Joda-Time classes, etc.
|
Mark M. |
however, at the high-level, this one TextView might want to show that date in a particular user format (e.g., YYYY-MM-DD), so you have code that converts the Date into the string representation that you want to show
|
Mark M. |
in some cases, you can avoid having a third tier of objects depending on how you populate your UI (e.g., data binding expressions and binding adapters)
|
Mark M. |
the details of that will vary a lot based on how you want to populate your UI
|
Aaron |
I see, makes sense
|
Mark M. |
View paste
|
Mark M. |
the fact that Reddit's API has that nasty set of nested stuff really shouldn't be the viewmodel's concern
|
Aaron |
View paste
|
Mark M. |
right
|
Nov 3 | 4:45 PM |
Aaron |
I'll fix it
|
Mark M. |
on the other hand, stuff like your formatCommentDetails() is reasonable, because thing like "we want a dot here" isn't a model thing, but rather is a UI thing
|
Aaron |
OK
|
Mark M. |
your decodeHtml() seems to duplicate code from just a few lines above it -- getCommentBodyHtml() probably should call decodeHtml()
|
Mark M. |
(and, sometime next year when you move to AndroidX, you can use HtmlCompat and eliminate that version check)
|
Aaron |
already switched it over to AndroidX, and clearly I am not taking advantage of it, because I didn't know it afforded a way to eliminate version checks
|
Mark M. |
it's more that they *finally* released an HtmlCompat class
|
Mark M. |
(only about six years after they should have)
|
Aaron |
I see
|
Mark M. |
most of the ...Compat classes in the support library/AndroidX are there to deal with version incompatibiltiies
|
Mark M. |
er, incompatibilities
|
Mark M. |
HtmlCompat, in particular, handles the changes they made to Html in N, so you can use HtmlCompat and it will "do the right thing" for you
|
Nov 3 | 4:50 PM |
Aaron |
nice, I'll look into these classes
|
Mark M. |
going back to your opening pasted comments, "One particular overarching question is where I should abstract more" -- if you literally mean abstract classes, I don't know that you necessarily need a ton
|
Aaron |
I didn't really realize that AndroidX had anything new, I thought for now it was mostly just a reorganization, I'll read up on that
|
Mark M. |
however, going back to my tiered models stuff, I would consider the middle tier to be an "abstraction" -- an idealized in-memory representation of the data that you need
|
Aaron |
OK
|
Mark M. |
well, most of the ...Compat classes have been around for a while, and did just get repackaged
|
Mark M. |
HtmlCompat happens to be new
|
Mark M. |
https://commonsware.com/blog/2018/05/29/at-last... for a few notes from me on HtmlCompat
|
Mark M. |
regarding MainActivity, I think the pattern going forward is to avoid all those listeners
|
Mark M. |
your activity can have its own viewmodel, which your fragments can access (in addition to their own "local" viewmodel)
|
Mark M. |
that activity viewmodel can have LiveData representing events that are being raised from the fragments that the activity should react to
|
Aaron |
OK
|
Aaron |
aside from the listeners,
|
Nov 3 | 4:55 PM |
Aaron |
does it make sense that I made MainActivity the "nexus" of launching different fragments?
|
Mark M. |
if you mean as opposed to fragments launching fragments, yes
|
Aaron |
rather than having them launched from various other places, it seemed helpful to consolidate it all somewhere, so I picked there
|
Mark M. |
that's perfectly reasonable
|
Aaron |
OK
|
Mark M. |
and, depending on what UI you might want to do for larger screens, that approach might be essential
|
Aaron |
tablet support is on my list, eventually, but it seems lower priority than learning RxJava
|
Mark M. |
in your case, your NoSurfViewModel is already shared, so you can add LiveData to it to reflect events that are in the fragments' UI but have effects that transcend the individual fragment, such as those necessitating a FragmentTransaction
|
Mark M. |
for your purposes, RxJava is more important than is tablet support, I'd say
|
Aaron |
yep, I think RxJava and unit/integration testing are the highest priorities, learning some Kotlin second, everything else third
|
Mark M. |
we're just about out of time -- any questions?
|
Aaron |
roughly...
|
Aaron |
not really, I'll need to research some of this stuff further to implement it, but everything you said makes sense
|
Mark M. |
that's reasonable
|
Aaron |
thanks a lot, this was very useful
|
Mark M. |
happy to help!
|
Nov 3 | 5:00 PM |
Aaron |
a few random questions but I'll save them for next time
|
Aaron |
have a good weekend!
|
Mark M. |
OK
|
Mark M. |
you too!
|
Aaron |
toodles
|
Aaron | has left the room |
Mark M. | turned off guest access |