Jan 3 | 7:20 PM |
Mark M. | has entered the room |
Jan 3 | 7:25 PM |
Mark M. | turned on guest access |
ryan7 | has entered the room |
Mark M. |
hello, ryan7
|
Mark M. |
how can I help you today?
|
ryan7 |
Hi Mark!
|
Jan 3 | 7:30 PM |
ryan7 |
Long message I pretyped, just a warning
|
ryan7 |
View paste
|
Mark M. |
first, thanks for the kind words!
|
Mark M. |
in terms of Q1, that's a pretty decent list
|
Mark M. |
I'd generalize MVP/MVVM to "some GUI architecture"
|
Mark M. |
we "talk a good game" in Android development about MVC, MVP, MVVM, MVI, etc., but in reality, we tend to wind up with MVS (Model-View-SomethingOrAnother)
|
ryan7 |
No problem! I find myself comparing the books you've created to other paid resources (Big nerd ranch, Professional android) and yours almost always seems to be more up to date and have better prose.
|
Mark M. |
"up to date" is what I aim for, though the past year has been a bit rough on that front
|
Mark M. |
back to Q1: for example, I wouldn't sweat "should I demonstrate MVP vs. MVVM?" -- pick what you're comfortable with, and be able to explain your choice in an interview
|
ryan7 |
Well that's understandable, the past 2 years google has put out so much new stuff.
|
Jan 3 | 7:35 PM |
ryan7 |
Ok, yea I was just starting off with MVP, then seeing the pros/cons firsthand
|
Mark M. |
learning what you like and dislike, and being able to explain them, should be more valuable than the actual bits and bytes of your demo code
|
Mark M. |
after all, it's reasonably likely that you'll be joining a project with an existing code base, as opposed to one that is starting from scratch
|
Mark M. |
so you'll need to adapt to whatever GUI architecture they're using, and that implies that you understand the *why* of your MV* choice more than the mechanics of its implementation
|
Mark M. |
in terms of other stuff for Q1: have Kotlin on your short list, along with some image loading library (Glide, Picasso, etc.)
|
Mark M. |
similarly, have something reactive on your short list: LiveData, RxJava, or perhaps Kotlin coroutines
|
Mark M. |
View paste
|
Jan 3 | 7:40 PM |
Mark M. |
but if you hit your list, you'll be doing pretty good
|
Mark M. |
note that I haven't been hiring junior developers, or been keeping tabs on expectations for them
|
Mark M. |
and, of course, the technologies we have discussed are for kinda "standard fare" apps -- in LA, if you want to do more media stuff, spend time with ExoPlayer
|
Aaron | has entered the room |
Mark M. |
similarly, games are a whole 'nuther ball of wax
|
ryan7 |
Thanks Mark!
|
Mark M. |
(BTW, hello Aaron -- I will be with you shortly!)
|
Aaron |
hello Mark, take your time
|
ryan7 |
Unfortunately I have to take off, but thanks for your very detailed answers
|
ryan7 | has left the room |
Mark M. |
ryan7: in terms of Q2, I specifically try not to provide a lot of advice for GUI architectures anyway
|
Mark M. |
OK... Aaron, how can I help you today?
|
Aaron |
so, my app is getting pretty close to a "finished" state, at least as finished as I am planning to make it, I've mostly been focused on learning some basic Kotlin and unit testing this last week, and I have a few open-ended questions today mostly related to those
|
Aaron |
here they are:
|
Aaron |
View paste
(2 more lines)
|
Aaron |
mostly just looking for sanity checks/feedback on things I don't know that I don't know that I don't know
|
Aaron |
:D
|
Jan 3 | 7:45 PM |
Mark M. |
in terms of #3, I would describe MV* and clean architecture as related but orthogonal
|
Mark M. |
depending on your implementation, MV* might get a bit more into clean architecture beyond the presentation layer
|
Mark M. |
IMHO, though, that's an architecture smell
|
Mark M. |
for example, clean architecture really calls for true POJO/POKO classes as your model objects
|
Mark M. |
and so things like Retrofit responses and Room entities would not count and would be treated more as data transfer objects (DTOs)
|
Mark M. |
however, a lot of apps don't bother with that sort of separation
|
Mark M. |
I guess I would phrase it as: in a code base adhering pretty strongly to clean architecture, I would expect MV* to affect the presentation layer mostly
|
Mark M. |
bearing in mind that I don't claim to be an expert on clean architecture, or on MV* for that matter
|
Aaron |
ok, that is pretty much what I've gathered, so that's a good enough answer for me
|
Jan 3 | 7:50 PM |
Mark M. |
in terms of #4, your util package has a bit more in it than I'd like, compared to the overall number of classes in the project
|
Mark M. |
util tends to wind up as a "junk drawer", and so I try to minimize what goes in one
|
Aaron |
yep, I'd say that's pretty much what it is
|
Aaron |
any particular suggestions on how to split it up?
|
Mark M. |
one way of minimizing that is to have more than one utils package, where you have a bunch of stuff that is tied more closely to one layer
|
Mark M. |
so, for example, in addition to the top-level utils, you could have ui.utils
|
Mark M. |
where data binding, WebView, and other UI-ish stuff might go
|
Aaron |
ah yes, that seems reasonable
|
Mark M. |
in your case, I suspect that most of that stuff would wind up in ui.utils, which means it didn't improve today's code that much
|
Mark M. |
however, it might help as the project grows
|
Mark M. |
and, as you find a few classes with commonality, you can move them to peer subpackages under ui
|
Jan 3 | 7:55 PM |
Mark M. |
top-level utils packages tend to be the junkiest of junk drawers; having a few smaller such packages tied to specific areas at least categorizes the junk
|
Jan 3 | 7:55 PM |
Aaron |
makes sense, I'll do that
|
Mark M. |
anything else before I start firing off code review comments for the classes you have in #1 and #2?
|
Aaron |
nope, go ahead
|
Mark M. |
note that I'm only reviewing from the GitHub repo, not from an IDE, so I'll be adding some weasel words to my comments :-)
|
Mark M. |
for example...
|
Mark M. | |
Mark M. |
if those are only used by this class, make them private
|
Mark M. |
(from GitHub, it's a pain for me to tell if they're being used elsewhere)
|
Aaron |
got it, no problem
|
Mark M. | |
Mark M. |
since Kotlin makes its equivalent of anonymous inner classes even easier than in Java, you might consider whether the activity should implement OnSharedPreferenceChangeListener, or should it hold an object that does
|
Aaron |
talking about object expressions?
|
Mark M. |
yes
|
Mark M. |
IIRC, OnSharedPreferenceChangeListener follows the single abstract method (SAM) pattern
|
Jan 3 | 8:00 PM |
Mark M. |
so you can just use a lambda
|
Mark M. |
val listener = SharedPreferences.OnSharedPreferenceChangeListener { key -> TODO() }
|
Aaron |
OK, will look into it
|
Mark M. | |
Mark M. |
prefer val to var -- immutability is your friend
|
Mark M. |
in this case, I think you only ever assign a value here in the declaration
|
Aaron |
yep, correct, that's a mistake
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- not really Kotlin-related, but the comment's explanation is kinda scary
|
Mark M. |
stuff before super.onCreate() is worrisome and should be minimized where possible
|
Mark M. |
Dagger notoriously needs it, so that's fine
|
Mark M. |
but if there's a way you can change pickDayNightMode() so it can be after super.onCreate(), you'll have an easier time with code reviewers :-)
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- if you wanted, you should be able to replace the get() with [] notation -- ViewModelProviders .of(this, viewModelFactory)[MainActivityViewModel::class.java]
|
Jan 3 | 8:05 PM |
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- I'm old-school and would rather see an if here (I reserve Elvis for assignments and stuff)
|
Aaron |
OK
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- if you haven't done so, consider setting up Android Studio's Kotlin code formatting rules to the Kotlin standard in Settings, then reformat your code
|
Mark M. |
IIRC, in standard Kotlin formatting, that closing parenthesis would go on the next line
|
Aaron |
OK
|
Mark M. |
overall, the Kotlin community is trying to be a bit more anal about code formatting than we saw with previous-generation languages (e.g., Java), which is why I mention it
|
Aaron |
yes, good call
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- you can change that to if (code.isEmpty())
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- not Kotlin-specific, but I'm old-school and would rather see an else than a mid-function return, particularly for something this small
|
Jan 3 | 8:10 PM |
Mark M. |
nothing in SplashHelper leaps out at me, other than code formatting
|
Mark M. |
one top-level comment: you should consider adding copyright/license headers to these files
|
Mark M. |
skipping them for XML and stuff is OK, but for program code, it's good licensing form to have the license boilerplate on the files
|
Aaron |
I see, alright
|
Aaron |
is there a way in Android Studio to programmatically add that to all my files at once?
|
Mark M. |
not that I know of, but I haven't looked for it either
|
Aaron |
ok no worries
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- I assume "sut" is "system under test"; if so, consider "underTest" as a more readable property name
|
Aaron |
OK
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- if it's the same for each test, you could make the property a val and initialize it there, allowing you to skip the @Before function
|
Jan 3 | 8:15 PM |
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- are you sure that you want that client ID in the git repo? I don't know much about Reddit's API and terms/conditions
|
Aaron |
yes, it's OK
|
Mark M. |
you still might consider at least pulling it out of the Kotlin source, such as having it in gradle.properties and from there add it to BuildConfig
|
Aaron |
well,
|
Mark M. |
I assume that client ID is used elsewhere, so you should aim to define it only once
|
Aaron |
it is in my BuildConfig and that's where I'm getting it from in the production classes (along with several of the other variables that get built into that URL), what I was attempting to do in the test, is to provide assurance that if any of those variables accidentally get changed, the test will tell me
|
Mark M. |
ah
|
Mark M. |
that's reasonable, though you might want to add some comments to that effect, to help future you
|
Aaron |
as per that comment, that test isn't actually testing any logic of my own, it's just basically verifying the pieces of that URL
|
Aaron |
line 29 has the comment - does it not make sense?
|
Mark M. |
if it makes sense to future you, that's what matters
|
Mark M. |
I didn't interpret that as "the API key might have changed"
|
Aaron |
lol alright
|
Mark M. |
but, I don't know all of what is going in those URLs
|
Aaron |
ok, understood
|
Mark M. |
a general comment about the unit tests: unlike instrumented tests, we can use backtick function names with unit tests
|
Jan 3 | 8:20 PM |
Mark M. |
so, instead of this_tests_stuff(), you can use `this tests stuff`()
|
Mark M. |
you like fairly verbose test function names, and so being able to use whitespace may help with readability, both for the code and for the test output
|
Aaron |
ah yeah I read about that, I'll change it to take advantage
|
Mark M. |
unfortunately, something about AndroidJUnitRunner means we can't do that for instrumented tests
|
Aaron |
that reminds me of a question that came up in the BCG, actually
|
Mark M. |
https://github.com/ajh3/NoSurfForReddit/blob/ma... -- your test libraries may already have an assertEmpty() or similar function
|
Aaron |
ah yes definitely
|
Kent S. | has entered the room |
Mark M. |
or, since you seem to have Hamcrest, it'll have something for empty strings
|
Aaron |
(sidebar, p.1030, second to last paragraph of BCG, "You cannot package JUnit tests..." -> isn't that exactly what AndroidJUnitRunner does, lets you run instrumented unit tests on the actual device?)
|
Mark M. |
sorry, I'll need to look at that later
|
Aaron |
no problem
|
Mark M. |
let me take a question from Kent, since the chat is nearly over, and I'll return to you shortly
|
Mark M. |
Kent: hi! do you have a question?
|
Aaron |
sounds good
|
Kent S. |
hi Mark, I wanted to thank you for updating with Kotlin
|
Mark M. |
it'll be a sloooooooooooooooow process
|
Mark M. |
but, you're welcome!
|
Kent S. |
Your Java versions have been invaluable, I won't waste any of your time. Thank you for what you do :)
|
Jan 3 | 8:25 PM |
Mark M. |
thanks for the kind words!
|
Kent S. |
have a good night.
|
Kent S. | has left the room |
Mark M. |
OK, back to the review...
|
Mark M. |
overall, with your unit tests, other than what I mentioned above, I don't have any comments
|
Aaron |
alright, that's good
|
Mark M. |
I haven't used that particular Kotlin-over-Mockito library, so I'm not really able to comment on how you're using it
|
Aaron |
np
|
Mark M. |
note that you if want Hamcrest and Kotlin, there is Hamkrest
|
Mark M. |
not saying that you have to use it; just making sure you knew it existed
|
Aaron |
ok, haven't come across that yet, I'll take a look
|
Mark M. |
it's just a Hamcrest variant that is more Kotlin-native
|
Aaron |
alright, sounds good, well that was a lot of good info today, thanks for that
|
Mark M. |
with regards to the paragraph, I'm not sure what your question is
|
Mark M. |
instrumentation testing requires a development machine
|
Mark M. |
some developers have looked to have a way to exploit instrumentation APIs from a device-installed APK, which isn't possible
|
Aaron |
next time I might ask you to just poke around in the codebase in general with no specific questions, just to see if any macro-level problems pop out, but we definitely don't have to spend a whole hour on it, I really appreciate all the help but I don't want to continue using the chats so heavily
|
Jan 3 | 8:30 PM |
Mark M. |
so long as there aren't others here, you're welcome to the chat time
|
Aaron |
thanks :)
|
Mark M. |
and that's a wrap for today's chat
|
Mark M. |
the next one is tomorrow at 9am US Eastern, part of the scrambled holiday schedule
|
Aaron |
sounds good, talk later
|
Aaron |
won't be there for that, maybe the day after
|
Aaron |
night
|
Aaron | has left the room |
Mark M. | turned off guest access |