Jul 30 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
sudokai | has entered the room |
sudokai |
Hi Mark
|
Mark M. |
hello, sudokai!
|
Mark M. |
how can I help you today?
|
Jul 30 | 7:25 PM |
sudokai |
Hi Mark, we are interviewing an Android candidate to join our team.
|
sudokai |
What kind of questions would you ask?
|
sudokai |
Eager to hear your thoughts, given your experience.
|
Mark M. |
um
|
Mark M. |
my interviewing style is probably unusual, in part because it is old
|
sudokai |
Let's hear it :P
|
Mark M. |
I recently have had to help on some interviews, but prior to that, it had been 20 years since I was involved in hiring anyone
|
Mark M. |
so, I claim no particular expertise in the art of modern interviewing
|
Mark M. |
what level of candidate is this? junior? mid-level? senior?
|
sudokai |
But you have a lot of Android experience
|
sudokai |
Mid-level I'd say
|
Jul 30 | 7:30 PM |
Mark M. |
View paste
|
Mark M. |
this assumes that I have reviewed a resume/CV/LinkedIn profile, though I usually have questions based on what I see there
|
Mark M. |
my objective in an interview is mostly as a "BS detector" and to get a sense for how well they communicate
|
Mark M. |
the "BS detector" is to ensure that they really do have the background that they claim
|
sudokai |
They have to have worked with Kotlin, RxJava, ExoPlayer, Dagger, MVVM
|
sudokai |
Yeah, I see it the same way
|
sudokai |
As a "BS detector" I mean
|
Mark M. |
right, so I'd be asking them to describe their experiences with those things, and asking follow-up questions based on their answers
|
Mark M. |
I'm not a big "write some code for me" type of interviewer, in part because I wouldn't have the time for that and questions, and in part because we simply never do that sort of thing (write random code under pressure with an audience)
|
sudokai |
Yeah, I was surprised how hard FizzBuzz seemed for some people
|
Tad F. | has entered the room |
Tad F. |
Hi Mark
|
Jul 30 | 7:35 PM |
Mark M. |
hello, Tad!
|
Mark M. |
sudokai: let me take a question from Tad, and I will swing back to you in a bit
|
Mark M. |
Tad: how can I help you today?
|
sudokai |
Sure
|
Tad F. |
I have a question about Preferences.
|
Tad F. |
I haven't migrated to the jet pack approach yet
|
Tad F. |
In the classic approach, I am using preference headers: "General", "Sync", and something called "Media Site Access"
|
Tad F. |
Under the latter I have quite a few types of sites where the user can enter their credentials to allow the app to let them get at their media
|
Tad F. |
Dropbox, Instagram, FB, etc
|
Tad F. |
I really don't want one big preference screen for this
|
Tad F. |
What I want is something like "sub-headers"
|
Tad F. |
But I haven't been able to find anything
|
Tad F. |
The fragments for each to gather credential info are quite different
|
Mark M. |
are you sure that you really want that to be in a PreferenceFragment, then?
|
Tad F. |
They have different callbacks, some actually have to start a separate auth token process, so I capture on onResume call to keep going, etc.
|
Mark M. |
(or rather in a fragment showing a preference-style UI)
|
Tad F. |
Well, I was trying to capture a consistent "preference" UI experience across all these settings...
|
Tad F. |
do you have an alternative suggestion?
|
Mark M. |
I was going by "the fragments for each to gather credential info are quite different", which seems to imply that your consistent UI objective would be difficult
|
Tad F. |
I think if I could get something like a "sub-header" that would appear when the user chose "media site access", then the individual items on that sub-header screen could each be assigned their separate preference fragment that would work ok
|
Tad F. |
I meant that what the individual behavior is for each media set set of preferences actually do to gather the credentials is different
|
Jul 30 | 7:40 PM |
Mark M. |
I haven't had to do this in years and years, but AFAIK the AndroidX preference system still allows you to have a <Preference> that either ties to an Intent or is just one that you attach a listener to at runtime
|
Tad F. |
For example - Dropbox requires that you essentially start another activity - it accesses dropbox, then when it returns in onResume you can check for the status of the auth token
|
Tad F. |
Instagram is not like that at all
|
Mark M. |
yeah, I wouldn't have this in a preference screen
|
Tad F. |
Where would you have it?
|
Mark M. |
I'd use the preferences for other stuff and have an account manager fragment for presenting the list of accounts and how to add/edit/remove them
|
Mark M. |
whether that account manager fragment is linked to from the preference screen is up to you, though I'd probably give it more visibility than that
|
Mark M. |
but, regardless, a plain Preference should allow you to link to whatever you want
|
Tad F. |
So right now my drawer menu off the main activity has a "Settings" choice that launches the preference activity. You are suggesting I have a separate entry there for 'Account management' separate and distinct from the launch point for "Settings"?
|
Mark M. |
my gut says that yes, that's how I would do it
|
Tad F. |
Interesting...I'll think about that.
|
Mark M. |
it feels like accounts are a Really Big Deal for your app
|
Mark M. |
and I worry that not everyone bothers looking at "settings"
|
Tad F. |
They are important yes - they allow the user to choose media from their favorite social media sites for inclusion in the playlists my app builds
|
Tad F. |
From within the SAF
|
Tad F. |
(I'm writing documentsproviders for those sites that don't already have them)
|
Mark M. |
you probably then want to make it easy for them to set up a first account and easy for them to add new ones
|
Tad F. |
One other question - I seem to recall somewhere along the line that Ringtone Preference has been deprecated, or is not supported or some such in the new preference world, is that right
|
Mark M. |
unless you are doing a custom onboarding experience, both of those are probably simpler if they are not blended in with other preferences
|
Tad F. |
?
|
Mark M. |
correct, they never ported it to the AndroidX preference system
|
Jul 30 | 7:45 PM |
Tad F. |
Currently I'm allowing the user to select a ringtone for notification(s) of various sorts.
|
Mark M. |
you can create your own ListPreference for that populated at runtime
|
Tad F. |
Yeah, ok.
|
Tad F. |
Just more work ;)
|
Mark M. |
it's just that they didn't want the preference library to have a bunch of code related to looking up ringtones, as I recall
|
Tad F. |
RingtoneManager is still in place then, I take it
|
Mark M. |
I think so, but I haven't worked with it in ages
|
Mark M. |
let me take another question from sudokai, and I'll be back with you in a bit
|
Tad F. |
Sure
|
Mark M. |
sudokai: over to you! do you have another question?
|
sudokai |
Mmmm, still thinking about interview questions
|
sudokai |
Go ahead guys
|
Tad F. |
Mark - I've been reading some about the deprecation of AsyncTask, and am wondering if you can point me to examples of best practice replacements?
|
Mark M. |
sudokai: OK -- ping me if you're ready for another round!
|
Tad F. |
I saw an interesting post by EpicPandaForce on SO fairly recently...any input?
|
Mark M. |
um
|
Tad F. |
I am using it now for short lived tasks, always extending in a static class, always using weakref, etc.
|
Mark M. |
in terms of AsyncTask replacements, the best replacement is "rearchitect your app and getting that code the heck out of your UI layer"
|
sudokai |
I use WorkManager
|
Mark M. |
AsyncTask only ever really made sense if you were doing long-running work directly in an activity or fragment
|
Mark M. |
and in modern architectures, that's a no-no
|
sudokai |
now you can execute immediate work with the foreground service mode and there's no 10 minute restriction
|
Jul 30 | 7:50 PM |
Mark M. |
WorkManager has its benefits for "bigger than a breadbox" sorts of things, but I tend not to think of it as a replacement for AsyncTask
|
Tad F. |
Really? The docs specifically mention it isn't for long running tasks...I've actually never used it that way because I was worried that even using a threadpoolexecutor the queue would fill up
|
Mark M. |
for transactional sorts of work (Web service calls, database I/O, etc.), the typical approach nowadays is a reactive framework: Kotlin coroutines, RxJava, etc.
|
Tad F. |
when you say "getting that code the heck out of your UI layer", what is "that code"? threading?
|
Mark M. |
more or less, yes
|
Mark M. |
saying that you use Glide or Picasso for image loading directly in the UI layer is fine
|
Tad F. |
Wait - so you are advocating for no threading in the UI layer?
|
Mark M. |
correct
|
Mark M. |
the recommended architecture pattern is that stuff moves into repositories and data sources
|
Mark M. |
they handle the I/O and the threading needed for that I/O and deliver results reactively
|
Tad F. |
What I'm using it for is to gather various bits needed when a user chooses a media item from a resource where it is going to take some time to download, process, etc.
|
sudokai |
I use RxJava
|
Tad F. |
So Glide oriented (and in some cases I partially use Glide)
|
Mark M. |
for album art, that's reasonable
|
Tad F. |
I also use it to validate login to the website
|
Mark M. |
some things, like OAuth, are unavoidably tied to the UI layer
|
Tad F. |
Throw up an indeterminate progress bar, do the validation in the thread, and return yes/no
|
Tad F. |
validate login to the app by hitting the website, I should say
|
Mark M. |
and for that, RxJava or coroutines would be the replacement for AsyncTask
|
Jul 30 | 7:55 PM |
Mark M. |
and in your case, a lot of your "heavy lifting" presumably is in your DocumentsProvider implementations
|
Tad F. |
Yes - there is a thread in there that fetches doc info in batches
|
Tad F. |
Which actually brings me to my last question, unless sudokai you are ready to jump in?
|
Mark M. |
Tad: I think you can go ahead
|
Tad F. |
I'll give an example about the documentsprovider I just did for Instagram
|
Tad F. |
In that case, their API allows you to specify a "limit" in the call for doc info, let's say it is 10
|
Tad F. |
So they return 10 items, and they also return a "paging" bit of json that lets you know a url for the "previous" page and "next" page
|
Mark M. |
that's a reasonably common REST pattern
|
Tad F. |
But i haven't been able to translate this into any API on the DocumentsProvider side
|
Tad F. |
I am looking for a way to signal to the provider framework that I'm not done, but I need it to jog me when the user "pages down" past the current end to see if there is any more.
|
Tad F. |
Not finding anything
|
Jul 30 | 8:00 PM |
Mark M. |
I doubt that there is such a thing
|
Tad F. |
You can signal the framework to keep pinging you to get more entries, and then signal it to stop doing that.
|
Tad F. |
But that's it.
|
Mark M. |
DocumentsContract was not set up with paging in mind
|
Tad F. |
So my thread currently would just keep spinning until Instagram didn't include a "next" paging
|
Tad F. |
Which if you have 1000's of photos is gonna be a problem
|
Tad F. |
So what is the recommended way to utilize SAF and hit a site that has 1000's of entries?
|
Tad F. |
hit a resource, I guess I should say
|
Mark M. |
well, please understand that I would not be at all surprised if you are one of fewer than 100 developers worldwide who is trying to do this sort of thing
|
Mark M. |
the DocumentsContract model is more of local filesystems, SMB/CIFS, and simple document stores (your Google Drives and Dropboxes of the world)
|
Tad F. |
Even if we used it on the local device, it is possible for there to be tons of files, no?
|
Mark M. |
sure, but they are usually organized into directories
|
Mark M. |
and the DocumentsContract model adopts that tree approach
|
Mark M. |
if the user is silly enough to have tons of files in a single directory, and the I/O is a bit sluggish, that's really their own problem
|
Tad F. |
Dropbox certainly acts that way
|
Tad F. |
But Instagram, Flickr, pintrest, FB, etc. don't
|
Mark M. |
my best guess is that you might need to keep a local cache of the metadata that you synchronize periodically
|
Mark M. |
but that's just a guess
|
Mark M. |
again, I doubt your desired use cases were what the DocumentsContract people had in mind
|
Tad F. |
Local disk cache, I guess you are meaning. I have a cache now that the thread feeds into, but it is memory based
|
Jul 30 | 8:05 PM |
Mark M. |
yes, I was assuming a persistent cache, that would survive between uses of your provider (whether used by your app or other apps)
|
Tad F. |
Even with a disk based cache, what you populate is rows in a cursor - so it ends up going into memory anyway
|
Mark M. |
I assumed that the concern was avoiding network I/O for calls that would require multiple pages from the SDK
|
Mark M. |
so, the network-based SDK would be feeding the cache, and the cache would be what your DocumentsProvider API works off of
|
Tad F. |
No - the concern is the model now just grabs all files that are available, and for resources that aren't "directory" focused, I am concerned about overloading the memory consumption of the app.
|
Mark M. |
ah
|
Mark M. |
I think the "all files that are available" may need to be revisited :-)
|
Tad F. |
Yeah - hence my original question about any way to have the DP framework support a paging model
|
Mark M. |
no, I was thinking more in terms of virtual directories
|
Mark M. |
even for some service that is more based on tags or something, you can set up virtual directories based on a tag, to limit the number of items you have to deal with at a time
|
Mark M. |
or virtual directories based on date
|
Mark M. |
or virtual directories based on other metadata
|
Jul 30 | 8:10 PM |
Tad F. |
I was just thinking along those same lines - but the tricky part will be back in the UI knowing when the set of entries I've passed back is really at the "end", so I could enable/disable a "next set" button or something.
|
Tad F. |
I don't control the chooser UI
|
Mark M. |
and you wouldn't need to, as there is no "next set" button
|
Mark M. |
the UI displays everything in a directory
|
Mark M. |
you need to set up your tree such that a directory is reasonably-sized for both you and the user
|
Tad F. |
Yes, that's right
|
Tad F. |
I'll have to come up with a scheme in the provider to create faux directories I guess
|
Mark M. |
that's my guess
|
Tad F. |
Ugh.
|
Tad F. |
OK
|
Mark M. |
IOW, figure out a tree structure for the data, and use that to drive your directories
|
Tad F. |
Well, most of those providers just give you a flat list of however many media items the user has uploaded.
|
Tad F. |
So I'll just pick some number - say "every 100" and create a new directory internal to the provider
|
Mark M. |
will the user be able to make sense of that?
|
Tad F. |
Well, I'll have to come up with something more creative than "first 100", "second 100", :)
|
Mark M. |
I was assuming that there was some metadata about the items (tag, dates, etc.) that would make logical divisions for you and the user
|
Tad F. |
Not sure what other choice I haev
|
Tad F. |
Not consistently
|
Tad F. |
Maybe date
|
Mark M. |
rolling back a bit in the advice, your on-disk cache would be of the relevant metadata options
|
Mark M. |
it doesn't need to be consistent across providers
|
Mark M. |
it only needs to be consistent within a provder
|
Tad F. |
I'm doing a year/month/week recyclerview already in the app for media they have included in their playlists.
|
Mark M. |
er, provider
|
Tad F. |
So I could present the provider the same way
|
Jul 30 | 8:15 PM |
Mark M. |
that's a possibility
|
Tad F. |
Sudokai - any questions from you?
|
Tad F. |
I actually have one more, but will wait
|
Tad F. |
It's gonna be tricky to do that date thing, because many don't deliver them to me by date order.
|
Tad F. |
But I'll look into something like that
|
Tad F. |
Back to RxJava - I haven't used this yet. It is an entire "way of doing things"? i.e. is this a platform I'll need to learn?
|
Mark M. |
um, kinda
|
Mark M. |
I mean, it's not an OS
|
Mark M. |
but reactive programming in general is a bit of a philsophy
|
Mark M. |
and RxJava has a particular implementation of that philosophy
|
Tad F. |
I've heard about it - just haven't used it. I've done a lot of threading in Java over the years, written my own thread pools, yada yada. I've also done some stuff with Promises in Node js
|
Mark M. |
RxJava is basically a set of thread pools (schedulers) with a managed callback system (subscribers)
|
Tad F. |
Do you discuss it in any of your books?
|
Mark M. |
I have a chapter on the basics of RxJava in *The Busy Coder's Guide to Android Development*
|
Tad F. |
ok I'll check it out.
|
Tad F. |
As always, thanks for your input!
|
Mark M. |
it's a bit thin, insofar as there are whole books just on RxJava
|
Tad F. |
Bye
|
Mark M. |
see you later!
|
Tad F. | has left the room |
Jul 30 | 8:20 PM |
Mark M. |
sudokai: if you have any other questions, go right ahead! I assumed that you didn't have any, since you were not chiming in
|
sudokai |
Yeah I was looking at top questions on Stackoverflow
|
sudokai |
:D
|
sudokai |
Are any advantages of coroutines over RxJava?
|
Jul 30 | 8:25 PM |
Mark M. |
IMHO, they are substantially easier to learn
|
Mark M. |
and they are a bit more natural to read
|
Mark M. |
for example, in most cases, normal try/catch for exception handling "just works"
|
sudokai |
Any performance advantages?
|
Mark M. |
I haven't seen a performance comparison
|
sudokai |
Well, I find the coroutine scope stuff utterly confusing
|
sudokai |
it's nice if you don't need to care: viewModelScope
|
Mark M. |
agreed
|
Mark M. |
for sophisticated stuff, if you are having to create your own scopes... yeah, it's confusing
|
sudokai |
Anyway, thanks for the advice
|
Mark M. |
for a lot of developers, stock Jetpack scopes handle most of the cases
|
sudokai |
I still haven't looked into Dagger Hilt
|
sudokai |
There's a lot of hype
|
sudokai |
but I use Koin
|
Mark M. |
I've glanced at it, nothing more
|
sudokai |
The only problem with Koin is that it's not typesafe
|
Mark M. |
I'm a Koin fan as well, though I expect that I will use Hilt in *Elements of Android Jetpack* for a Java DI example
|
sudokai |
Dagger seems super hard
|
sudokai |
I remember Spring for web. That's easy and beautiful.
|
sudokai |
Dagger has always seemed monstruous to me
|
Mark M. |
Dagger is aggravating to get set up, and so the hope is that Hilt will make that a bit better
|
Mark M. |
and once you have enough Dagger set up, a lot of the ongoing stuff is copying boilerplate
|
sudokai |
These days I try to stay away from the hype
|
sudokai |
:)
|
Jul 30 | 8:30 PM |
Mark M. |
I try to minimize worrying about pre-release Jetpack libraries
|
sudokai |
When I see something new, I ask myself: does this help me build better apps?
|
Mark M. |
I have a rough enough time dealing with pre-release OS versions and IDEs
|
sudokai |
Like things I couldn't do before
|
sudokai |
Or does it simplify my code by an order of magnitude?
|
Mark M. |
(and, admittedly, I am working with a pre-pre-pre-release Jetpack Compose, just 'cause it's gonna be huge)
|
sudokai |
Have you looked at Flutter?
|
Mark M. |
briefly
|
sudokai |
I know it's Flash for mobile phones, but it's looking promising
|
sudokai |
It's so easy to create interfaces that are pixel perfect for both platforms
|
sudokai |
I haven't looked at Jetpack Compose yet
|
sudokai |
I'm subbed to your newsletter though
|
sudokai |
I'm still going through the motions of catching up
|
sudokai |
Many years of Android stuff to go through
|
sudokai |
I know Jake Wharton is super against Flutter and technically right
|
Mark M. |
Compose will be huge, but unless you really want to kick the tires on it, there's no reason to pay attention to it until a 2021 beta, or at least a couple-months-from-now alpha
|
sudokai |
But people don't care
|
sudokai |
The little I saw of Compose, I liked
|
Mark M. |
I was just worried about Flutter being too tied to Fuchsia early on
|
Mark M. |
nowadays, Flutter has enough momentum that it might survive even if Google pulled the plug
|
sudokai |
My point is that people like Jake say Flutter should die in hell, but users don't care
|
Jul 30 | 8:35 PM |
Mark M. |
I generally agree
|
sudokai |
Users don't care if you use MVVM, OOP, whatever
|
Mark M. |
I worry a pinch about the iOS platform fidelity, as it's not like Apple is "on board" with Flutter
|
Mark M. |
but, for a lot of users, platform fidelity doesn't really mean a ton
|
sudokai |
Flutter is superior to React Native because it renders everything it's flash
|
sudokai |
With React Native, you always get weird quirks
|
Mark M. |
to an extent, Compose is Flutter for Kotlin, insofar as they both take the same approach (direct Skia canvas rendering)
|
sudokai |
Like you use CSS to specify box shadow for iOS, but on Android you need to use elevation
|
sudokai |
Yeah, that's the part I liked
|
Mark M. |
but, alas, I need to continue on with the rest of my evening
|
sudokai |
This thing of mixing XML and Java and the names are different, oh there's a bug unfixed for 5 years, etc.
|
sudokai |
Have a good evening!
|
Mark M. |
you too! the next chat is Thursday, 4pm US Eastern!
|
sudokai | has left the room |
Mark M. | turned off guest access |