Android: Sharing Resources in Eclipse

The resources of an Android project are difficult to share between projects. In order to create a common library of widgets that reference resources is no simple task and yet is something quite desirable. Creating a “Save File” generic dialog and being able to share it amongst various Android projects not only as an Activity, but as a class ancestor that can be descended from and modified for various specialized cases is a highly desirable feature. I am used to having such a feature in the past with other languages (like Delphi’s Forms) and have been frustrated with Android projects in Eclipse. The various techniques available to potentially share resources all have their fatal flaws.

The Problem

  • Android Library Project – While it is true that these projects can contain resources and other projects can reference this library, the only actual sharing being performed here is the sharing of Manifest-level objects such as Activities, Services and Providers. You cannot access the individual classes nor resources contained within it. Since access is not possible, deriving a new class based on any class contained within the Library Project is not possible either.
  • Linked Res Folder – You can set up a shared folder containing resources you would like to share across projects and link to such a folder inside each project that requires them. The problem with this approach is that resource folder names have special meanings for the Android packager and would require all your projects to share all their contained resources. Such a system would bloat each project horribly since it would have to contain all the resources for every project in such a shared folder.
  • Linked Res File – You can create a singular resource file link between projects by using Ctrl+Shift+Drag&Drop on a library project’s resource file into another project. The link would exist, but the Android packager that generates the R class that contains a reference to all the resources will not follow the linked file and therefore ignore it. Since the linked file was ignored, the resource cannot be referenced by the project that contains the link. May as well not even create the link since it has no effect on the project.
  • JAR library – External JAR libraries cannot contain resources at all.
  • Build Path – Adding files or folders of resources to the build path will allow you to access them in code, but they will not be packaged into the final APK since they are considered external libraries, rendering the technique useless.

Resource Filename Discovery

I decided to change my tactics. Instead of trying to find a way to share the same resource file across multiple projects, I would instead find a way to easily copy resource files from my library containing the resources into the projects that would require them. Drawables, layouts, menus, and some xml resource files are pretty easy to copy where needed. Value resources such as strings are also easily copied once I found out that multiple value resources with different xml file names are allowed (with Eclipse 3.6 Helios, I don’t know about earlier versions). The trick here is to create multiple string resource files in your library with each one pertaining to a particular subject. Take the example of a save file dialog with a layout file called dialog_save_file.xml. That layout file will most likely contain references to a few strings. Instead of placing those strings in an xml file called “strings.xml” in the “values” folder of your library, call the file strings_dialog_save_file.xml. Once you have the library strings file, you are free to copy that file into all the projects that use the library’s save file dialog. The strings contained within the file will be merged into the R class along with all the other strings the project uses. While we lose the ability to change one resource file and have it automatically affect all other projects that use it which forces us to manually update all projects that use any “shared” resources that get modified, we gain the ability to include library references to resources that should not be shared between projects and yet would exist as a resource identifier in all your apps anyway – like a drawable named app_icon or a string named app_name. If you use such things, make sure you do not overwrite them if you later re-copy resources from your library project into your app. I would merge those kinds of strings into the standard strings.xml file directly so that the temptation to overwrite later is non-existent.

R Class Conundrum

Android projects consider resources to be private and unique to the project that contains them. It is required to be like that because of how the R class gets generated. Also, because resources are referenced using this generated R class, any library code that tries to reference a resource via the R class would use the “wrong” one. Consider our shared file save dialog class which would be contained in a library namespace of com.mycompany.android.dialog, if a class tried to reference R.layout.save_dialog, then that R class would be of the same namespace as com.mycompany.android.dialog. Sharing the code with another project would mean that R class referenced in the library code would interpreted as the application project’s R class. Since the namespaces of the application project’s R class would be different from the library code’s namespace, the compiler will force the library code to create a line importing the application project’s R class. Now your library code would contain a reference to a specific project’s R class which won’t be contained in other projects that want to use this library code as well, thus breaking the ability to share the library code with other projects.

 

Indirect R Class Referencing

Library code referencing the R class or other projects would render the code useless for any other projects. So how do we reference R class Resource Ids in our library code in such a way as to compile in every project? Let me introduce you to Resources.getIdentifier()! Any place in your library code that would usually reference R.id.some_resid can be replaced with a call to getIdentifier(“some_resid”,”id”,getPackageName()) instead. The method is discouraged by Google as being inefficient and therefore probably pretty slow. Keep this in mind when writing your code and save off the result for later use so the impact of the inefficient method is kept to a minimum. Aside from that caveat, you are now free to write library code referencing resources in a way that avoids the R class and therefore allows your code to be shared amongst several projects.

Developing a Library Widget

When I develop a widget meant to reside in the library, I first implement it in the project that I’m working in so that resources are easily made and referenced with the R.id.some_resid mechanic. I write the code such that the R class resource ids get copied into a local variable during initialization and use the local variable throughout the rest of the class. Once I am happy with the result, I move the code and resources into the library and replace all my references to the R class with a method call. Something like:

1
getString(R.string.some_resourceid)

would become

1
 getString(getResId("string","some_resourceid"))

where getResId is defined as

protected int getResId(String aResType, String aResName) {
    return getApplication().getResources().getIdentifier(aResName,aResType,getPackageName());
}

If I have a lot of them, I may do some further optimizations, but the quick replacements are a great way to get started to realize the potential.

Parting Thoughts

Using these techniques of writing widgets and storing library resources allows me to reuse them in several projects with ease. My code becomes more efficient, flexible and maintainable. Best of all, I gain the feature of creating library widgets that can be used or descended from in several different apps. I wish such a feature was a standard part of the working environment, but until it becomes a standard feature, I will make do with what I have available.

3 thoughts on “Android: Sharing Resources in Eclipse

Leave a Reply

Your email address will not be published. Required fields are marked *