Renaming the Android Manifest package

I recently needed to be able to change the package name of an app at build time. This is a common need when you have a paid and a free version of an app. It's also useful if you want to be able to install multiple versions of an app on your phone, such as a "dev" and a "stable" build.

One way to do it is to transform the whole project as a library project, and then create one final project for each version, that depends on the library project.

Aapt magic

There is another way: aapt has a --rename-manifest-package parameter that rewrites the package name of the binary AndroidManifest.xml file in the final APK.

Here is what aapt help says:

   --rename-manifest-package
       Rewrite the manifest so that its package name is the package name
       given here.  Relative class names (for example .Foo) will be
       changed to absolute names with the old package so that the code
       does not need to change.

The great advantage is that your code won't change, the R class stays identical.

Ant

Since r17, this option is available in the aapt Ant task, through the manifestpackage attribute. You'll need to override the -package-resources target, copied from the SDK build.xml:

<target name="-package-resources" depends="-crunch">
  <do-only-if-not-library elseText="Library project: do not package resources..." >
    <aapt executable="${aapt}"
     manifestpackage="com.my.package"
    >
...
    </aapt>
  </do-only-if-not-library>
</target>

Maven

The android:apk goal of the android-maven-plugin has a renameManifestPackage parameter.

One last thing

If you load some resource ids at runtime, you may need to update your code.

I used to do this:

String packageName = context.getPackageName();
Resources res = context.getResources();
int id = res.getIdentifier("my_drawable", "drawable", packageName);

This usually works great, especially in library projects where you do not know the package name.

However, the problem here is that the resources were processed before the package name was finally updated. So packageName is wrong.

It's easy to fix though, by retrieving the package name of another resource with Resources.getResourcePackageName().

Let's create a resource id dedicated to that purpose, for example in res/values/ids.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <item name="used_for_package_name_retrieval" type="id"/>
</resources>

And now we get the right package:

Resources res = context.getResources();
String packageName = res.getResourcePackageName(R.id.used_for_package_name_retrieval);
int id = res.getIdentifier("some_drawable", "drawable", packageName);

Conclusion

This tip helps creating different versions of the same app.

As you can see on this screenshot, the package name can then be set as a build parameter, to create parameterized builds in Jenkins.

Comments