Office Hours — Today, December 5

Thursday, December 3

Dec 5
3:55 PM
Mark M.
has entered the room
Mark M.
turned on guest access
4:00 PM
Kai H.
has entered the room
Kai H.
Hello
Mark M.
hello, Kai!
how can I help you today?
4:05 PM
Kai H.
I have several apps that share functionality and was wondering what your best practice for modularisation is.
Mark M.
that depends a lot on the scope of "share functionality", I suppose
Kai H.
(given a legacy app that is quite coupled so far and has nothing like dependency injection or such)
Mark M.
if the apps are 90+% overlapping, with minor variations, that probably should be a single app and using product flavors for the variations
less than that, but the apps are all part of a single project, then library module(s) would be my answer
if the apps are largely independent, in separate projects, but share a bit of code, create a utility library project that they all pull in via a shared Maven repo
Kai H.
Two sets of apps have some functionality in common, like a file system abstraction, some utility functions, a FileProvider and such.
And there is the case of different "versions" of one app, with more features the younger the app is but a lot of shared functionality.
4:10 PM
Mark M.
your first case sounds like my third scenario (utility library pulled in via Maven repo)
your second case, frankly, I've never dealt with
Kai H.
Did you ever have a customer with different versions of a backend or server and an app for each server? Server version 6 has app version 6, server version 7 has app version 7 and so on. And each app supports the added featuers of the newer server.
Mark M.
unless there are 7 different Play Store listings, IMHO that's one app
Kai H.
There are 7 different Play Store listings :D
Mark M.
then, I'm back to: I've never dealt with that
Kai H.
But some got removed recently because of the "repetitive content policy" so we might end up with your first scenario eventually.
Mark M.
it doesn't sound like a particularly great setup
4:15 PM
Kai H.
It's the "new server, new desktop client => new app => make new branch from old app, versionstrings++" setup
Mark M.
yeah, on the whole, that setup does not sound pleasant
and in terms of how you would reuse code between those "versions", I can't really answer it in the abstract
Kai H.
It is especially exciting when you can't merge things up because too much has changed between versions.
;-)
Mark M.
yeah, well, that's why my reaction is: it should be a single app, with pluggable strategy patterns for dealing with different backend versions
Kai H.
So I think I would like to go scenario 1 and scenario 3
Mark M.
so, product flavors for the "versions" within a set and a library for the shared stuff between sets?
Kai H.
Exactly.
4:20 PM
Mark M.
OK. Product flavors may be tricky for the "versions", but I can't rule that out.
Kai H.
What do you mean? And are the "build variants" or something else?
Mark M.
taking the second part first, "build variants" are the cross product of "build types" and "product flavors"
so, suppose that you have the two stock build types of "debug" and "release"
and you have two product flavors of "chocolate" and "vanilla"
(note: product flavors do not have to be named after real flavors)
your build variants are: vanillaDebug, vanillaRelease, chocolateDebug, and chocolateRelease
each build variant combines one of the build types with one of the product flavors, and the overall set of build variants represents all possible combinations
Kai H.
I see
Mark M.
in terms of my "tricky" term... it comes back to how you tweak an app in a product flavor
you're used to your code mostly being in a source set named main/
4:25 PM
Kai H.
Yes.
Mark M.
perhaps you also put some code in debug/, or *maybe* release/ source sets, particularly for integration with some libraries like LeakCanary or Flutter
product flavors give you another axis for source sets: vanilla/ and chocolate/
Kai H.
I see. And depending on which flavour I chose, the code from the folder is loaded.
Or used, rather.
Mark M.
right, so a vanillaDebug build would blend main/, debug/, vanilla/, and vanillaDebug/ source sets, or whatever of those that you happen to have
the catch is in *how* those get blended
with resources, flavor-specific source sets replace main on a per-resource basis, so if you have an app_name string resource in vanilla/, it would take precedence over the app_name string resource in main/
nice and simple
that's not how it works with Java/Kotlin source code
Kai H.
That would be too simple and predictable I guess X)
Mark M.
for that, main/ can reference code that will appear in a flavor, and then each flavor needs the implementation of that code
Kai H.
That sounds somewhat usable
Mark M.
you might define the API for that code in main/ as an abstract class or interface, that the flavors then use
4:30 PM
Mark M.
so, for example, suppose main/ has MainActivity, an IServerStrategy interface, and assumes that the flavors supply a ServerStrategy implementation of that IServerStrategy interface
4:30 PM
Kai H.
I have to do that by myself? I thought I could just import a class and then have it twice, once in each flavor, and then that will be used?
Mark M.
the problem is that main/ does not know anything about the flavors
Kai H.
I see :/
Mark M.
main/ can know that there will be a ServerStrategy class, and it implements IServerStrategy
Kai H.
Sounds like inviting a lot of complication and trouble.
Mark M.
so main/ can do IServerStrategy thingy = new ServerStrategy();
and then work with thingy as an IServerStrategy object
both vanilla/ and chocolate would implement their own ServerStrategy, with their own rules
*those* ServerStrategy classes could, in turn, refer to other code that is in the flavor or that is in main/
the net is that you wind up with a few "entry points" from main/ into the flavors, each flavor implements those entry points, and then the flavors can do what they want from there
but, yeah, this can get complicated
it tends to be used for cases like "white-labeling", where N customers all get the same basic app with resource changes, a dedicated application ID, and a bit of varying code
for example, a business that helps restaurants create dedicated apps for presenting menus -- that might be really one app with one flavor per restaurant
4:35 PM
Kai H.
I think I'll try not to have to use that strategy :D
Mark M.
it's an available technique, but it's use case is for massive overlap -- I think I used 90% as the value earlier in the chat
Kai H.
Btw, how does my android studio know of a "build" and a "release" build variant when there is only "buildTypes { release {..." in the build.gradle?
Mark M.
debug and release are built-in build types
you can add others beyond that if needed
conversely, no flavors exist by default -- if you want them, you have to create them
so, the build variants == the build types in the absence of any flavors
Kai H.
Ok
4:40 PM
Kai H.
We already use a library as a module in two different apps. It is pulled in from a relative directory via settings.gradle.
Mark M.
OK
that's not as nice as the Maven repo approach, from a versioning standpoint, as I think we discussed previously
Kai H.
Which means everyone working on the app needs to have the libraryin the same relative directory or he will have to change the settings.gradle X)
Mark M.
but, it works
Kai H.
Exactly
Do you agree that the ways to do modularization in Android are a bit lacking?
4:45 PM
Mark M.
not really
Kai H.
Ok
Mark M.
your "set of versions" circumstance is unusual
and there could be more "out of the box" ways to set up a team Maven repo (which, in the end, is just a directory tree with some metadata)
the problem is that the details behind "team Maven repo" can start to get complicated, depending on where the developers are
for example, my primary customer has a Maven repo in an Amazon S3 bucket, akin to what I did with my CWAC series of libraries
but, getting the Amazon S3 security right for public read access, as in my case, was a lot simpler than getting it right for private read access, as in my customer's case
Kai H.
That can get quite complicated I guess.
Btw, I have a hard time wrapping my head around how "but the apps are all part of a single project, then library module(s) would be my answer" would look like. Is a project a "git project"? Or a project in Android Studio? Why would one want that to begin with?
Mark M.
for that, I meant an Android Studio project
and, in terms of why... specifically for easy reuse of modules
Kai H.
I can't really imagine how several apps in one AS project would look like.
4:50 PM
Mark M.
um, instead of app/ you have AwesomeApp/ and NoMyAppIsTheAwesomeApp/ and OhYouMustBeKidding/ and so on
along with AwesomeLib/ and MyLibIsWayBetter/ and so on
Kai H.
Sounds like a harmonic development team was at work ;-)
I see
Feels...wrong.
Mark M.
most of my book projects are set up this way -- in that case, mostly for a "monorepo" of all the samples
and in terms of "feels... wrong", some teams have dozens or hundreds of modules, representing one or a few apps and a bunch of libraries
Kai H.
So I should reconsider?
Mark M.
I can't answer that
I can give you options, and I can tell you pros and cons
and, sometimes, I can give specific advice, particularly for narrow scenarios
Kai H.
What are the cons of monorepos, in your experience?
Mark M.
well, my use of a monorepo per book does not really compare to the sorts of monorepos you read about
and I haven't worked on a massive project in a monorepo, so my experiences also do not really compare
Jan
has entered the room
Mark M.
hi, Jan!
Jan
Hi, Mark.
Mark M.
Jan: we're nearing the end of the chat hour -- do you have a question?
4:55 PM
Jan
View paste
In my View Model I have this:val markers = MutableLiveData<MutableList<FFGoogleMapAnnotation>>()
   init {
        markers.value = mutableListOf()
        }

I set up to observe:

         mainViewModel.markers.observe(viewLifecycleOwner, Observer {
                    addMarkersToMap(it)
                })

My debug shows the list changing (using .add on the list) but the observe never triggers (other than onCreate when list is empty)
Mark M.
bear in mind that LiveData only emits when you give it a new object
IOW, I would use List, not MutableList
Jan
Ok. Thanks.
Mark M.
with MutableList, you will be tempted to just manipulate the *contents* of the list, which will not trigger your Observer
if either of you have any last questions, fire away!
5:00 PM
Mark M.
OK, that's a wrap for today's chat
the next one will be Tuesday, in the 7:30pm US Eastern slot
have a pleasant day!
Kai H.
Thanks and the same
Kai H.
has left the room
Jan
has left the room
Mark M.
turned off guest access

Thursday, December 3

 

Office Hours

People in this transcript

  • Jan
  • Kai H.
  • Mark Murphy