Making Free and Paid Versions of an Android App
A common pattern for mobile application developers is to release a full, paid version of an app, and then also release a limited-functionality (lite) free version. The key to doing this is keeping the code for full and lite versions from creating a maintenance nightmare.
On BlackBerry, I have accomplished this goal by keeping one codebase for both full and lite versions. That codebase contains a superset of the functionality in both versions. Typically, the lite version simply disables some features, hides some UI, and possibly adds a button to allow easy upgrading to the full version. RIM provides native support for preprocessor directives. Preprocessor directives typically spark a firestorm of religious opposition, but in reality, when there are only a handful of lines of code needed to differentiate versions, and you want to ensure that code is not present in the released product, processor directives can often accomplish the task most easily and maintainably. Unfortunately, Google has clearly stated that they will not be supporting preprocessing for Android.
Another option is to make a common library (jar) that contains the bulk of the functionality. Then, you can maintain separate Android application projects that use this library, and customize its behavior. This is the strategy I wound up implementing.
In the case of this example, I started with an existing application, the SkeletonApp sample from the Android SDK, which represents the full/paid version. I am then going to build a lite version off of that. In your case, you may be able to plan ahead, and build both full and lite versions at the same time. In this case, you may choose to put the vast majority of the functionality in one common lib, and then build two small apps to use it (full and lite). Nevertheless, the technique shown for building a lite version should be easily extendable to such a design.
Some limitations of the implementation I chose are:
- Installing the full version does not remove the lite version, since they have different (package) names. This may be a good or bad thing, depending on your needs.
- All the functionality is provided in the lib used by the lite version. If you're super-concerned about hackers unlocking features, you should simply be aware of this.
- In the sample code I provide, the full version must have one line changed in the
project.propertiesfile to build full vs. lite versions. - This is perhaps the biggest limitation: if your original app uses
assets, they will not be directly available to the code running in the lite version. Code and layouts share fine, but not assets! You will have to manually copy the assets into both versions.
Download
Howto
- Using Eclipse, open a workspace with the original/full app version.
- Choose File-> New-> Other ... -> Android Project. Name the project SkeletonAppLite, and make the default namespace
com.example.android.skeletonapp.lite. - Copy the
AndroidManifest.xmlfile from the SkeletonApp project, to the new SkeletonAppLite project. Although this project doesn't use them, according to this source, you should also copy over thelocal.propertiesandbuild.propertiesfiles. - In the lite version AndroidManifest.xml, rename the package to
com.example.android.skeletonapp.lite. - In the lite version AndroidManifest.xml, rename main Activity to
.SkeletonAppLiteActivity. - In your case, if you have Activities listed in your equivalent of the lite AndroidManifest.xml, that come from the original/full project, you may need to explicitly name them like
com.example.android.skeletonapp.MyActivity, instead of simply.MyActivity. - In the lite version
strings.xmlresource, change theapp_namestring to reflect this lite version (if you want it to appear differently to the user). - In the lite version main activity, I changed the inheritance from
Activitytocom.android.example.skeletonapp.SkeletonActivity.. This allows my lite Activity subclass to configure its base class, identifying that the code will be functioning in "lite" mode. - In the lite
SkeletonAppLiteActivity, I removed the call tosetContentView(), since the base class, SkeletonActivity, will be determining which view to load. - In the lite
SkeletonAppLiteActivity, I implement a constructor, and use that to tell the base class that this instance is the lite version. I implement that by adding a protected setter and member in the base class./** * Allow subclass to set this instance to full (true) or limited (false) functionality * @param value if true, then this is the full version */ protected void setFullVersion(boolean value) { mFullVersion = value; } - In the full version, I change the project type to library, in order to allow the lite app to use it. Do this by adding this line to (full version)
project.properties:android.library=true
- In the lite version, I reference the full version as a library. Do this by adding this line to (lite version)
project.properties:android.library.reference.1=../SkeletonApp
Be mindful of the relative path reference (../) if your directory structure doesn't get laid out with full and lite projects in parallel/sibling directories. You can also do this graphically in Eclipse by right-clicking the project and selecting Properties -> Android -> Library -> Add ... , and picking the full project, assuming it's available in your Eclipse workspace (which it should be). - In the eclipse Project Explorer, right-click the lite project, select Properties -> Project References and make the lite project reference the full library project.

- In the Project Explorer, right-click the lite project, select Build Path -> Configure Build Path ..., select the Projects tab, and Add the full project.
- Add your lite version icons to
res/drawable. - Remove the unnecessary
main.xmlfrom the lite project'slayoutfolder. - Build and run! Your final eclipse workspace should look like this:
With this setup, I can now selectively disable, or enable code in the full version library project, by using the mFullVersion member variable:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Disable the clear button if this is not the full version
((Button) findViewById(R.id.clear)).setEnabled(mFullVersion);
if (mFullVersion) {
((Button) findViewById(R.id.upgrade)).setVisibility(View.INVISIBLE);
}
}
In this example, I took the skeleton_activity.xml layout, and added a button that lets users upgrade to the full version. However, in the SkeletonActivity source, I hide this button when the full version is being run.
Now, in order to build the full version, I must comment out the full version project.properties line that specifies the project as a library (eclipse also has a graphical UI for unchecking this - Is Library).
This is a small headache, but it's not easy to forget, since the full version will not build an .apk unless you change it back. Unchecking one box and rebuilding may also be easier than maintaining a whole separate app project for the full version, which would entail maintaining 10 to 20 new files. However, that choice is up to you. You can either keep the primary project as I have, switching it between app and library, or maintain three projects: one primary library, and two thin app projects (full and lite).
Posted at 08:41PM Nov 13, 2011 by Nathan in Coding | Comments[0]
HTML allowed/encouraged in comments! Your email address is only used to notify you when your comment is accepted or rejected, and if there are more comments. I do not sell email addresses, or record them for any other purpose (notice the lack of advertising anywhere on my website).
