Managing Multiple Apps within a Single Android (Studio) Project

Chukwuemeka Nwagu
The Startup
Published in
5 min readJun 19, 2020

I work on a project that has assumed an interesting structure, so I want to share the experience. The requirement is that two different apps would be distributed, but in addition, one of the apps is supposed to contain all the functionality of the second app. A simpler way to put it is that we want some particular functionality of the main app to be distributed as a separate standalone app.

On the click of a button within app A, a new task should be started with the launch activity of app B. However, only a single apk file must be distributed for app A.

I saw this as a challenge of

  1. Setting up shared resources for two applications. (This is necessary because, by design, both apps A and B would share several UI components)
  2. Making the code and resources of the second application available in the first/main application.

After stumbling about with various options, I architected this by having the two application modules in a single project.

As is well known, in Android Studio, a project typically contains modules where one is an application module (“:app”) and perhaps one or more other are library modules. Also, an application module cannot be added as a dependency to another application. So how was I able to make this multiple-app-module structure work? In this article, I will describe the process to make two simple apps with a similar module structure. Then I will talk about some of the (un)expected behaviors we encountered with that structure and how we handled them.

So we will start with a new Android Studio project with the default app module in place. The first step is to add the second application module, which we will call side-app. So File > New > New Module… For type, Select Phone & Tablet Module. On the next form, we choose the module name to be side-app. Then next form, we choose an empty activity. Next form, we call the Activity SideActivity. (Remember that the goal is to have access to this activity in the app module). Finish the creation of side-app.

Then we have to create two more library modules. One should be named common-lib to contain shared resources between the two application modules, then another library module to be a shadow module for side-app in the app module. We will call this shadow module side-app-shadow.

Now open up the build.gradle files for the four modules to manage interdependencies.

First, side-app should implement common-lib.

implementation project(“:common-lib”)

app needs the common-lib too, but it would get it through the side-app-shadow. So side-app-shadow gets:

api project(“:common-lib”)

while app gets

implementation project(“:side-app-shadow”)

Now to do the actual “shadowing” of the side-app, we use Gradle source sets. Basically, we are going to tell side-app-shadow to get its code, resources, and asset files from the side-app module directory. To do this, go to the build.gradle for side-app-shadow and add the below code within the android {} block

The above effectively syncs files from the side-app to side-app-shadow. side-app-shadow is now seen to contain the package com.example.side_app in addition to its own com.example.side_app_shadow.

This is good. However, the gradle files and manifest files for these two modules still need to be manually kept in sync. Any new dependency added to side-app should be added to side-app-shadow (using the api scope if the app module also requires it.) Similarly, any new permission, activity, service, or receiver added to the manifest for side-app should be added to side-app-shadow.

After this, SideActivity (together with any other class, resource, or asset that is created in side-app) can be referenced within the app module and can be launched with an intent.

Both applications now show up on the configurations for debugging on a device. Also, the two apks are generated when you build debug apk(s) from the build menu.

In the following parts, I will discuss some issues that came up due to this architecture and how they were handled in the project. The first is with App classes.

Multiple Application Classes

We needed to have classes that extend Application in both app and side-app, and both these classes contain code that we want to be executed on launch.

For the side-app module, the class is made open

open class App : Application()

For the app module, we extend side-app App

class App : com.example.side_app.App()

And add

android:name=".App"

to both app and side-app manifests (but not side-app-shadow) between the application tags.

Glide’s GlideModule

The second nagging issue we faced was with the Glide image loading library. Both application modules make use of glide and have one class each that extend AppGlideModule and is annotated with @GlideModule. I find that Glide complains that: Type com.bumptech.glide.GeneratedAppGlideModuleImpl is defined multiple times

I tried to solve this by adding an exclude to the java scrDir

exclude '**/MyGlideModule.kt'

But, for unknown reasons, this did not solve it. The workaround I used involved having only one GlideModule, in the shadow module, and then a few other tweaks that are peculiar to my usage of Glide, so I will not share it here.

Here is a Github repo containing the app generated in this article.

If you think there is a more efficient way to achieve the same goal, please leave a note in the comments. Thanks for reading!

--

--