The source of this problem is Java itself, where you can't have things similar to #ifdef and most compiler don't allow completely replacing a class or part of it either.
I actually have a base project from which I build 6 APKs already, and more soon.
Each APK uses its own package name, however the whole code is under a single package, but that doesn't create any issue.
There's however many issues:
- All resources, all classes will be in the target APKs, whether used or not.
- Different behavior means different code or different resources, each of which must be handled differently.
For resources, it's "quite easy", just make replacement resources in the final application projects. However unused resources will be left over.
For classes, that's where it becomes very complicated.
To create slightly different behavior you can use reflection and interfaces, each APK implementing it's own version of the interface using a common name eg myActivityPrj which is retrieved using reflection.
Sometimes it's also easier to test the APK package name from within the code, so in the Application object, I set some boolean as to which APK is actually running. Making sure to use those only from the UI to avoid any performance hit.
To create very different behavior, for example an activity is not used in an APK, well, could use above method and a flag saying available or not, but what a waste of space inside the APK!
Another option is to break-down the central project in smaller pieces, if at all possible!
To actually make APK smaller, I found only one way so far: create a Java project which will copy the central project and then remove and/or replace files within this copy, from a remote source tree (a directory named "replacement-files" which contains a res and src folder).
For resources, that java project will actually parse all strings.xml and remove unused strings from a hard-coded list, can't trust lint output as some resources are used in sub-projects and lint doesn't care.
So far, the APK that includes all features is about 10MB, one variation is about 4MB, whereas it should actually be less than 2MB. Another variation is about 8MB, whereas it should really be 2 or 3MB. The above method is overly complicated and being able to removed unused code and resources is getting more and more complicated.
I've looked at various other solutions, but they're all very complicated with a lot of restrictions, constraints.
After 4 years of development on Android, my only conclusion is that Java is the poorest choice possible for a mobile device: slow, inefficient, resource hungry, and very inflexible. Looking at the NDK, it appears very inconvenient and very limited.