Now that we've finished creating this beautiful RenderBox
library, how do we reuse it in other projects? This is where
modules and .aar
files come into play. There are a number of ways to share code between Android projects. The most obvious way is to literally copy pieces of code into the next project as you see fit. While this is perfectly acceptable in certain situations, and in fact should be part of your normal process, it can become quite tedious. What if we have a bunch of files that reference each other and depend on a certain file hierarchy, such as RenderBox
? If you're familiar with Java development, you might say, "Well, obviously just export the compiled classes in a .jar
file." You would be right, except that this is Android. We have some generated classes as well as the /res
folder, which contains, in this case, our shader code. What we actually want is an .aar
file. Android programmers might be familiar with .aidl
files, which are used for similar purposes, but specifically to establish interfaces between apps, and not encapsulate feature code.
To generate an .aar
file, we first need to put our code inside an Android Studio module with a different output than an app. You have a few options from this point onward. We recommend that you create a dedicated Android Studio project, which contains the RenderBox
module as well a test app, which will build alongside the library and serve as a means to ensure that any changes you make to the library don't break anything. You can also just copy the renderbox
package and the /res/raw
folders into a new project and go from there, but eventually, you'll see that a module is much more convenient.
You might think "We're gonna call this new project RenderBox
," but you might run into a snag. Basically, the build system can't handle a situation where a project and module have the same name (they would be expected to have the same package name, which is a no-no). If you call your project RenderBox
, (technically, you shouldn't have if you followed the instructions) and include an activity, and then create a module called RenderBox
, you will see a build error that complains about the project and module sharing a name. If you create an empty project with no activity called RenderBox
and add a module called RenderBox
, you happen to get away with it, but as soon as you try to build an app from this project, you'll find that you cannot. Hence, we suggest that your next step from here is to create a new project called RenderBoxLib
.
Let's give it a shot. Go to File | New | New Project. Name the project RenderBoxLib
.
We don't need a MainActivity
class, but we're still going to want one, as discussed, as a test case to ensure that our library works. Adding a test app to the library project not only gives us the convenience of testing changes to the library in a single step, but also ensures that we cannot build a new version of the library without ensuring that an app that uses it can also compile it. Even if your library is free of syntax errors, it might still break compilation when you include it in a new project.
So, go ahead and add an Empty Activity, and click on Finish in the default options.
All familiar territory so far. However, now we're going to create a new module:
Instead of performing the next steps in Android Studio, let's just use our file manager (Windows Explorer or Finder, or the terminal if you're a pro) to copy our RenderBox
files from the existing project into the new one. If you're using version control, you might consider transferring your repository to the new project, or creating an init commit before the copy; it's up to you and how much you care about preserving your history.
We want to copy all your RenderBox
code to the new module from the RenderBoxDemo
project's /app/src/main/java/com/cardbookvr/renderbox
folder to the /renderbox/src/main/java/com/cardbookvr/renderbox
folder of RenderBoxLib
.
The same goes for the resources; copy them from the RenderBoxDemo
project's /app/src/main/res/raw
folder to /renderbox/src/main/res/raw
.
This means that almost every .java
and .shader
file that we created in the original project goes into the module of the new project, in their corresponding locations.
We won't be transferring MainActivity.java
, or any of the XML files, such as layouts/activity_main.xml
or AndroidManifest.xml
to the module. These are app-specific files, which are not included in the library.
Once you've copied the files, go back to Android Studio, and click on the Synchronize button. This will ensure that Android Studio has noticed the new files.
Then, with renderbox
selected in the hierarchy panel, initiate a build by navigating to Build | Make Module 'RenderBox' (or Ctrl + Shift + F9). You will see a bunch of errors. Let's take care of them.
RenderBox
references the Cardboard SDK, and as such, we must include it in the RenderBox
module as a dependency in a similar way to how we do it for the main app, like at the beginning of this project:
common.aar
and core.aar
library files to your project as new modules, using File | New | New Module... and Import .JAR/.AAR Package.RenderBox
model, using File | Project Structure. In the left-side panel, select RenderBox
, then choose the Dependencies tab | + | Module Dependency, and add common and core modules.Once you sync the project and trigger a build, you will hopefully see those errors related to CardboardView
and so on disappear.
Another build. Still, other errors?
This is because of the naming issues mentioned earlier. If the package name of your module doesn't match the package name from the original project (that is, com.cardbookvr.renderbox
), you will have to rename it in the copied Java files. Even if these match, we named our original project RenderBoxDemo
, which means that the generated R class will be part of the com.cardbookvr.renderboxdemo
package. Any import references to this package will need to be changed.
Start by deleting the line that references com.cardbookvr.renderboxdemo
(such as the Material
Java files). Then, any references to the R class will show up as errors:
Delete this line, and Android Studio will generate a new valid import line. Try and build it again. If it's error-free, we're good to go.
You will now see references to R show up as errors with a suggestion:
If you go ahead and press Alt + Enter, Android Studio will add the appropriate import line to your code. If you don't see the Alt + Enter tooltip, try placing your cursor next to R. Using the feature this way, you'll have to select Import Class from the menu you see after pressing Alt + Enter. If you still see errors, make sure that you've copied the shader code into the /renderbox/res/raw
folder, and that there aren't other errors interfering with this process. Essentially, we are removing any external references from the code and getting RenderBox
to build on its own. We can also accomplish this code fix by simply pasting import com.cardbook.renderbox.R;
over import com.cardbook.renderboxdemo.R;
. That's probably easier than the first method, but then you wouldn't have learned about Alt + Enter.
Once this is done, we should be able to build without errors. This might seem like a messy way to work, but it doesn't hurt to get messy once in a while. You might even learn something new about the build pipeline you didn't know earlier.
If everything goes well, you will see a file called renderbox-debug.aar
in renderbox/build/outputs/aar/
. If so, you're done. Whew!
One final thought: you should include renderbox-release.aar
in your final applications, but you will lose useful debugging features in the meantime. We will not discuss how to switch back and forth between debug and release in this book, but understanding build configurations is essential to the publication process.
This new project houses the renderbox
module, but there's also an app
folder that we created in the first place. app
is where we can implement a test application to make sure, at a minimum, that the library is built and basically runs.
We're going to do the same thing to the app module in RenderBoxLib
that we did in our new projects (like renderbox
, app
is a module. It turns out that we've been using modules the whole time!):
app
folder, go to Open Module Settings, and add the existing renderbox
module as a Module dependency with Compile Scope. Notice that the dependencies cannot be circular. Now that renderbox
is a dependency of the app, the reverse cannot be true./res/layout/activity_main.xml
and AndroidManifest.xml
, as we saw at the top of this chapter. (If you're just copying code, make sure that you change the package=
value to the current name, for example, com.cardbookvr.renderboxlib
).class MainActivity extends CardboardActivity implements IRenderBox
.MainActivity
class to instantiate RenderBox
and define a setup()
method, just like MainActivity
in RenderBoxDemo
. In fact, just go ahead and copy the entire MainActivity
class from RenderBoxDemo
, and make that you do not copy/overwrite the package definition at the top of the new file in your new project.With any luck, you should be able to click on the green run button, select your target device, and see a running app with our buddy, the vertex color cube. We've officially gone backward in terms of the final result, but our application-specific code is so clean and simple!
Now that we've gone through all of this trouble, let's do a trial run to see how to use our pretty little package all tied up with a bow. One more time. You can perform the following steps to start each of the subsequent projects in this book:
MyCardboardApp
, for API 19 KitKat. Include Empty Activity.RenderBoxLib/renderbox/build/outputs
folder of your RenderBox
lib project, and select the .aar
file. We recommend that you rename the module to renderbox
, as opposed to renderbox-debug
. Click on Finish. For a production app, you would want to have two different modules in your project: one for debug and one for release, but we will only be using debug for the projects in this book.app
. Click on the plus tab on the right-hand side, and choose Module dependency:renderbox
:We now have a copy of the .aar
file in our new project's /renderbox
module folder. When you've made changes to the RenderBox
library, you just need to build a new .aar
file (build menu, MakeProject
), overwrite the copy in the new project, and trigger a project sync, or clean and rebuild if you want to be sure. The new project does not maintain a link to the build folder of your library output project.
The remaining steps required to setup a new project are as follows:
.aar
packages common
and core
, and add them as dependencies to the app./res/layout/activity_main.xml
and AndroidManifest.xml
, as we've just done for RenderBoxDemo
.MainActivity
class so that it extends CardboardActivity
and implements IRenderBox
, using the same code as before.MainActivity
class to instantiate RenderBox
and define a setup()
method, just like our MainActivity
class in RenderBoxDemo
. In fact, just go ahead and copy the entire MainActivity
class, and be careful not to copy/overwrite the package definition at the top of the file.Build and run it yet again. Bagged it! We can now proceed with the cool stuff.
A final word on the module process: there's more than one way to peel an orange. You could have just created a new module in the RenderBox
demo project, grabbed its output, and been off and running. You can also just copy source files around and try using Git submodules or subtrees to synchronize the sources. This page from the IntelliJ docs discusses some of the finer points as well (https://www.jetbrains.com/idea/help/sharing-android-source-code-and-resources-using-library-projects.html). We've also made certain decisions in terms of keeping the main activity and layout files completely application-specific, and including most or all of our shaders and materials in the RenderBox
module, instead of in application code. At any one of these decision points, there are pros and cons, and we recommend that you think carefully about how you structure your own code in future projects.