Dec 5 | 3:55 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Dec 5 | 4:00 PM |
Kai H. | has entered the room |
Kai H. |
Hello
|
Mark M. |
hello, Kai!
|
Mark M. |
how can I help you today?
|
Dec 5 | 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
|
Mark M. |
less than that, but the apps are all part of a single project, then library module(s) would be my answer
|
Mark M. |
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.
|
Kai H. |
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.
|
Dec 5 | 4:10 PM |
Mark M. |
your first case sounds like my third scenario (utility library pulled in via Maven repo)
|
Mark M. |
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
|
Dec 5 | 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
|
Mark M. |
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.
|
Kai H. |
;-)
|
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.
|
Dec 5 | 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"
|
Mark M. |
so, suppose that you have the two stock build types of "debug" and "release"
|
Mark M. |
and you have two product flavors of "chocolate" and "vanilla"
|
Mark M. |
(note: product flavors do not have to be named after real flavors)
|
Mark M. |
your build variants are: vanillaDebug, vanillaRelease, chocolateDebug, and chocolateRelease
|
Mark M. |
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
|
Mark M. |
you're used to your code mostly being in a source set named main/
|
Dec 5 | 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
|
Mark M. |
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.
|
Kai H. |
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
|
Mark M. |
the catch is in *how* those get blended
|
Mark M. |
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/
|
Mark M. |
nice and simple
|
Mark M. |
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
|
Dec 5 | 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
|
Dec 5 | 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();
|
Mark M. |
and then work with thingy as an IServerStrategy object
|
Mark M. |
both vanilla/ and chocolate would implement their own ServerStrategy, with their own rules
|
Mark M. |
*those* ServerStrategy classes could, in turn, refer to other code that is in the flavor or that is in main/
|
Mark M. |
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
|
Mark M. |
but, yeah, this can get complicated
|
Mark M. |
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
|
Mark M. |
for example, a business that helps restaurants create dedicated apps for presenting menus -- that might be really one app with one flavor per restaurant
|
Dec 5 | 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
|
Mark M. |
you can add others beyond that if needed
|
Mark M. |
conversely, no flavors exist by default -- if you want them, you have to create them
|
Mark M. |
so, the build variants == the build types in the absence of any flavors
|
Kai H. |
Ok
|
Dec 5 | 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
|
Mark M. |
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
|
Kai H. |
Do you agree that the ways to do modularization in Android are a bit lacking?
|
Dec 5 | 4:45 PM |
Mark M. |
not really
|
Kai H. |
Ok
|
Mark M. |
your "set of versions" circumstance is unusual
|
Mark M. |
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)
|
Mark M. |
the problem is that the details behind "team Maven repo" can start to get complicated, depending on where the developers are
|
Mark M. |
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
|
Mark M. |
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.
|
Kai H. |
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
|
Mark M. |
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.
|
Dec 5 | 4:50 PM |
Mark M. |
um, instead of app/ you have AwesomeApp/ and NoMyAppIsTheAwesomeApp/ and OhYouMustBeKidding/ and so on
|
Mark M. |
along with AwesomeLib/ and MyLibIsWayBetter/ and so on
|
Kai H. |
Sounds like a harmonic development team was at work ;-)
|
Kai H. |
I see
|
Kai H. |
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
|
Mark M. |
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
|
Mark M. |
I can give you options, and I can tell you pros and cons
|
Mark M. |
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
|
Mark M. |
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?
|
Dec 5 | 4:55 PM |
Jan |
View paste
|
Mark M. |
bear in mind that LiveData only emits when you give it a new object
|
Mark M. |
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
|
Mark M. |
if either of you have any last questions, fire away!
|
Dec 5 | 5:00 PM |
Mark M. |
OK, that's a wrap for today's chat
|
Mark M. |
the next one will be Tuesday, in the 7:30pm US Eastern slot
|
Mark M. |
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 |