One of the issues that has irked me for quite a while is the fact that there is no easy way to work with a set of shared code between apps in Eclipse that simultaneously gives me instant access to modifying the shared library and also the flexibility of including only the pieces of it that are needed while excluding the rest. Android Library Projects are a big step forward in helping to create a set of shared resources as well as code, but they still lack the flexibility of only including just what you need (not to mention Library Projects cannot use other Library Projects).
I’ve made a few small apps and every app includes the library I’ve built up over the years. Since the library has steadily increased in size, so too do the apps that use it even if they only use a very tiny portion of the library. There has to be a better way to share code between apps and yet not link in every file defined in the library. Exporting an APK is not smart enough to only include classes and resources actually referenced and used and Eclipse does not have a way to define a filter based on whether or not the project imports, exports or uses the class somehow. I have finally figured out a technique to use, though, while not a simple click-and-done process, it is fairly simple to setup and maintain. The benefit of following my technique results in apps that are only the size they need to be and will not bloat unnecessarily with unused classes.
Before you start using Library Projects in Eclipse, save yourself a lot of headaches and download the latest version of Eclipse. I was using version 3.4 Ganymede and found numerous issues with trying to create and use such projects which all got fixed when I upgraded Eclipse to 3.6 Helios. The upgrade was painless as I just downloaded the Eclipse IDE for Java EE Developers version, copied my eclipse folder to a safe place just in case and then unpacked the contents of the download over the top of my current install. Everything worked fine after I loaded up the IDE and ran a Check for Updates to get all the plugins up to date.
Once I had the latest version of Eclipse, I first create an Android Library Project by using the New Android Project wizard to create a normal project and then checking the Project Properties> Android tab> Is Library checkbox. A word of warning about naming your project – avoid spaces, dashes and punctuation except for the underscore “_”. Numerous people have reported lots of problems if you have a name that includes such things. I avoided them in my folder path as well just to make absolutely sure I would not run into any problems. I gave my project name “lib_com_blackmoonit” because I want to prefix all my library projects with “lib_” so they will group up together in Eclipse’s Package Explorer and I used my package namespace so that it will be unique and sort in a similar fashion with other libraries (most notably the License Verification Library supplied by Google that I have named “lib_com_android_vending_licensing” which started me down this particular rabbit hole in the first place).
At this stage, I have a Library Project named “lib_com_blackmoonit” with an Android target set to the minimum SDK supported by my library code (which is Android 1.5 in case you were curious). We want the project existing in this form for debugging and testing purposes which I will explain later. For now, the important part is that we have a project set to our minimum supported SDK version and a src folder we can now share out to all our app projects in a different manner.
Once I have my basic library project created and some files placed into the src folder, it is time to share that src folder with other projects. Open up ProjectA and go to it’s Properties> Java Build Path. On the Source tab you will find your project’s gen and src folders. From here we have to perform two steps.
- First, use Link Source… and locate the src folder of the previously created Library Project. The dialog will complain about src already existing so you will have to change the Folder Name to something else. I personally use “Blackmoon Android Library” as the Folder Name. You can use whatever you want as long as it does not conflict with any folder already being used in ProjectA. While there is a Next button to define filters, they will not work at this time so just click OK until the Properties dialog is also closed.
- Re-open the ProjectA Properties> Java Build Path> Source tab and now you should see the gen and src folders along with your new link to your library’s src folder. Now we edit the library link and define our filters. Either highlight the library link and click Edit and then Next on the following dialog or you can expand the entry and highlight the Included filter and click Edit to get to the same place.
Once there, click the Add Multiple button and select all the classes ProjectA will need, leaving out all the ones you don’t need. While this step is optional, I highly recommend it for the sole reason that your resulting apps will have a smaller footprint since you only include what you need instead of everything the library contains. Mobile devices have limited storage capacity and this step may have an effect in how people like your apps. This is not a one-time step as you will need to visit this particular area again whenever you add a new class to your library that your app uses. You may also find yourself performing this step several times as you figure out all the classes used by your app; you may be surprised at how many are required.
After you set up your linked source to your Library Project’s src folder and define an include filter for all the ones you need for ProjectA, your project will show your entire library as a separate folder inside ProjectA using the name you gave it when you set up the Folder Name in step 1. It will list all files you are using in the project first followed by all the ones in your library that are there, but are being ignored. For all intents and purposes, your shared code is now a part of ProjectA and you can work with it as easily as you can with any source file in your project. Duplicating the procedure with ProjectB will allow you to share your library code and instantly see the effects of any modifications to your shared code in both ProjectA and ProjectB (if they are both open). This technique also allows reuse of code in several separate Library Projects as well, which is a big deal for me since Library Projects cannot use other Library Projects.
If you add code to your library, you can do so from any project, not just your original library project. Once you create the file, you will need to add it to your Include filter so that your project can see it. Avoid the “Add to Build Path” menu option as it does not affect the filter we previously defined and will cause the file to disappear from your project. If you accidentally do this, you can recover from it by opening up the Project Properties> Java Build Path> Libraries tab and removing the file you accidentally added.
The original Library Project is never referenced nor used as a project since we are sharing the src folder out to the various projects that need it instead of using the “Project Properties> Android> Add Library” method. The reason you want to make your shared source a Library Project is that it proves helpful when developing new library classes because you usually will be working in a specific app project as you develop which will target the latest Android version or at least some version newer than what the library must also support. Downgrading app projects to test compilation in earlier versions is time consuming and problematic, but since the library project is always at the lowest version already, issues with library code can immediately been seen as it is being added/modified. In addition, you can create test units in the library project so that it does not clutter up your app projects.
While it is not as elegant a solution as having a linker smart enough to exclude classes you don’t need, it gets the job done with as little work on the developers part as possible while maintaining easy access to shared code and optimal app deployment at the same time. A side benefit to this technique, I’m finding, is that I am forced to create a better structure for my library so that the static utility function classes do not import all kinds of unrelated things without good reason and those routines that need a lot of imports are generally best served by moving them to a more appropriate class. This way, my small apps have become small again.