Kevin Schultz

Mobile Engineering

Using Android Content Providers With Multiple Package Names

One of the main benefits of switching to Gradle for your Android build system is the ability to create multiple packages from a single commit. This enables installing debug & release versions of the same app on a single phone, as well as more complicated flavor setups such as free/paid versions. Although setting up the actual package naming is simple, unfortunately some Android systems breakdown when the package name varies. One common problem is INSTALL_FAILED_CONFLICTING_PROVIDER errors when your app includes Content Providers.

Content Providers are accessed via Content URIs. The first portion of the Content URI is the Content Authority, which should correspond with the package name of the app. The Content URI has to match in both the Manifest and the definition of the Content Provider’s authority in Java. A typical implementation of a Content Provider’s manifest & Content URI definition for an app with a constant package name is below. (For a complete example of implementing a Content Provider I recommend Wolfram Rittmeyer’s post.)

Content Provider Contract
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * Defines contract between content providers and content consumers. A contract defines the
 * information that a client needs to access the provider as one or more data tables. A contract
 * is a public, non-extendable (final) class that contains constants defining column names and
 * URIs.
 */
public final class PlacesDB {

    /**
     * URI Matcher authority, has to be added to manifest identically in order
     * for content resolver to find the content provider
     */
    public static final String AUTHORITY = "com.kevinrschultz.placesapp.provider.places";

    private static final String SCHEME = "content://";

    public static final class Place implements BaseColumns {

        // URI definitions

        private static final String PATH_PLACES = "/places";

        private static final String PATH_PLACE_ID = "/places/";

        /** The content:// style URL for this table */
        public static final Uri CONTENT_URI = Uri.parse(SCHEME + PlacesDB.AUTHORITY + PATH_PLACES);

        /**
         * The content URI base for a single place. Callers must
         * append a numeric place id to this Uri to retrieve a place
         */
        public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME + PlacesDB.AUTHORITY + PATH_PLACE_ID);

        /**
         * The content URI match pattern for a single place, specified by its ID. Use this to match
         * incoming URIs or to construct an Intent.
         */
        public static final Uri CONTENT_ID_URI_PATTERN = Uri.parse(SCHEME + PlacesDB.AUTHORITY + PATH_PLACE_ID + "/#");

        ...

    }
}
src/main/AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- Permissions -->
    <application>
        <!-- Content Providers -->
        <provider
            android:name="com.kevinrschultz.placesapp.model.PlacesProvider"
            android:authorities=com.kevinrschultz.placesapp.provider.places"
            android:exported="false" >
            <grant-uri-permission android:pathPattern=".*" />
        </provider>
        <!-- Activities -->
        ...
        <!-- Services -->
        ...
    </application>
</manifest>

Generating multiple packages from Gradle is relatively straightforward. You can either specify the entire package name for a particular build variant or use a packageNameSuffix. The example below specifies the complete package name, but in practice I prefer setting the root of the package name in the manifest and then appending build variant specific suffixes. This example shows splitting the packages based on build flavor (free v paid), though this applies equally to splitting the packages based on build type (debug vs release), or some combination of both.

build.gradle
1
2
3
4
5
6
7
8
9
productFlavors {
        free {
            packageName "com.kevinrschultz.placesapp.free"
        }

        paid {
            packageName "com.kevinrschultz.placesapp.paid"
        }
    }

No matter how you decide to split the packages, setting up the Content Provider Authority is the same. Since the release of Gradle Plugin 0.7.0 the package name is automatically added to the BuildConfig file and is available for use in constants.

Content Provider Contract Authority
1
    public static final String AUTHORITY = BuildConfig.PACKAGE_NAME + ".provider.places";

If you are working with several build variants, it is important to understand how Gradle merges the various sources sets. For Manifests, the XML from main, buildType, and buildFlavor are combined. This means that you do not have to duplicate your entire manifest into each build variant folder. Leave the constant portion in src/main, and then only extract the variable portion to the build variant specific folders. The example below is for splitting the package based on build flavor (free/paid), but would also apply for splitting based on build type (debug/release). In this case, nothing about the Content Providers is in the debug & release specific Manifests. Note that you still must put the Content Provider definition within the application block in the build variant specific manifests.

The actual definition will have an authority derived from the package name, but the Content Provider class reference does not change. When you change the package name for your app it effects the .apk’s ‘package name’, but it doesn’t actually change the Java packages at all.

src/main/AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
    <!-- Permissions -->
    <application>
        <!-- Nothing about Content Providers at all -->
        <!-- Activities -->
        ...
        <!-- Services -->
        ...
    </application>
</manifest>
src/free/AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
    <application>
        <!-- Content Providers -->
        <provider
            android:name="com.kevinrschultz.placesapp.model.PlacesProvider"
            android:authorities=com.kevinrschultz.placesapp.free.provider.places"
            android:exported="false" >
            <grant-uri-permission android:pathPattern=".*" />
        </provider>
    </application>
</manifest>
src/paid/AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
    <application>
        <!-- Content Providers -->
        <provider
            android:name="com.kevinrschultz.placesapp.model.PlacesProvider"
            android:authorities=com.kevinrschultz.placesapp.paid.provider.places"
            android:exported="false" >
            <grant-uri-permission android:pathPattern=".*" />
        </provider>
    </application>
</manifest>

You can inspect the merged manifests in build/manifests/free/debug/AndroidManifest.xml and build/manifests/paid/debug/AndroidManifest.xml. If everything is setup correctly, the final merged manifest for any particular build variant will look almost exactly like the original single manifest, except with package name specific Content Authorities. This will allow you to run both apps on a single phone at one time.

How (Not) to Test Android’s Parcelable Interface

Android applications invariably seem to have many places where you have to pack data into an Intent or Bundle in order to pass it on to another component. I have found that packing a single Java object in the payload is far more maintainable than using several primitive key/value pairs. If you decide to add or remove an a field to the object later, you won’t have to go back and modify every single Intent & Bundle. In order to do this, the object must implement either Serializable or Parcelable. I generally prefer Parcelable, even though it takes a bit more code to implement.

Unfortunately I ran into a huge pitfall with Parcelable objects. I have been using a very basic unit test to ensure my Parcelable implementation was correct, and didn’t realize the test wasn’t actually proving everything was working as intended. The other day I was refactoring some code and seeing ClassCastExceptions when pulling a particular Parcelable object out of a Bundle. I couldn’t figure out where the type was getting switched in my code, but it never occurred to me that the actual Parcelable implementation was at fault because I had too much faith in my unit tests. After a couple hours of digging I figured out that the naive unit tests I have been using for Parcelable don’t actually ensure anything. Hopefully this post can help you avoid the same mistake.

First, a quick summary of implementing Parcelable if you have not done it before. In order to make an object Parcelable, you must handle writing to the Parcel, creating an object from the Parcel, and provide a CREATOR factory. This code is pretty straightforward although there are a few gotchas. Take a look at the implementation below for class ‘Foo’. Note that in order to test these types of classes I always make sure to write (or generate) a proper equals() method.

Complete (Correct) Parcelable Implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class Foo implements Parcelable {
    private long mId;
    private int mCount;
    private String mName;
    private boolean mFlag;

    /** Generic constructor */
    public Foo(long id, int count, String name, boolean flag) {
        this.mId = id; this.mCount = count; this.mName = name; this.mFlag = flag;
    }

    /** Parcelable constructor */
    private Foo(Parcel in) {
        this.mId = in.readLong();
        this.mCount = in.readInt();
        this.mName = in.readString();
        // Again, there is no readBoolean() method, so use an int or byte instead
        this.mFlag = in.readInt() == 1;
    }
    // Note that order of writing variables to parcel 
    // must exactly match order in parcelable constructor 
    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeLong(mId);
        out.writeInt(mCount);
        out.writeString(mName);
        // How hard would it have been to include a writeBoolean method? Come on
        out.writeInt(mFlag ? 1 : 0);
    }
    public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
        public Foo createFromParcel(Parcel in) {
            return new Foo(in);
        }

        public Foo[] newArray(int size) {
            return new Foo[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Foo)) {
            return false;
        }
        Foo foo = (Foo) o;
        if (mCount != foo.mCount || mFlag != foo.mFlag || mId != foo.mId) {
            return false;
        }
        if (!TextUtils.equals(mName, foo.mName)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = (int) (mId ^ (mId >>> 32));
        result = 31 * result + mCount;
        result = 31 * result + (mName != null ? mName.hashCode() : 0);
        result = 31 * result + (mFlag ? 1 : 0);
        return result;
    }
}

(Bonus rant: why did the Android team not include writeBoolean() and readBoolean() methods? I generally pack booleans as an int but you can also use a byte or a String to pass them. Thanks to this answer on StackOverflow for the inspiration on how to pass them cleanly.)

My original unit test naively mirrored how you actually use Parcelable objects. Stick it in a Bundle, pull it out of a Bundle, and check equality.

Incorrect Parcelable Unit Test
1
2
3
4
5
6
7
8
    // This doesn't actually test anything
    public void testParcelableInterface() {
        Foo foo = new Foo(1L, 3, "name", false);
        Bundle bundle = new Bundle();
        bundle.putParcelable("foo", foo);
        Foo parceledFoo = bundle.getParcelable("foo");
        assertEquals(foo, parceledFoo);
    }

Thus, the above test passed even though the Foo class’s CREATOR was written originally as shown below. Note that CREATOR is returning type Bar instead of type Foo. This is one easy pitfall when writing the boilerplate associated with Parcelable.

Incorrect Parcelable CREATOR type
1
2
3
4
5
6
7
8
    public static final Parcelable.Creator<Bar> CREATOR = new Parcelable.Creator<Bar>() {
        public Bar createFromParcel(Parcel in) {
            return new Bar(in);
        }
        public Bar[] newArray(int size) {
            return new Bar[size];
        }
    };

After finding the actual bug, my concern switched to figuring out why the unit tests hadn’t caught it. It turns out that Bundle doesn’t actually serialize/de-serialize each value until the Bundle itself is parceled. Thankfully James Wilson has a nice solution that correctly tests the object.

Correct Parcelable Unit Test
1
2
3
4
5
6
7
8
    public void testParcelable() {
        Foo foo = new Foo(1L, 3, "name", false);
        Parcel parcel = Parcel.obtain();
        foo.writeToParcel(parcel, 0);
        parcel.setDataPosition(0);
        Foo parceledFoo = Foo.CREATOR.createFromParcel(parcel);
        assertEquals(foo, parceledFoo);
    }

In fact, with this code the test suite won’t even build because CREATOR’s return value is type checked at compile time, rather than casting from Bundle’s getParcelable() at runtime.

I would recommend writing that unit test anytime you are creating a Parcelable object.

Sharing Android Debug Keystore

How often do you see the “Re-installation failed due to different application signatures” error when trying to install a development APK? By default the Android build chain uses a local debug signing keystore, so the same commit built on two different machines will be signed differently and can not re-install on top of each other. This is easily fixed by putting a shared debug keystore into your project repo. It’s a simple time saver if you sometimes use different computers, have a continuous integration server, or work with a team.

Unlike the release signing keystore, the debug keystore can be changed at any time. In fact they expire 1 year after creation, so if your project runs for a significant amount of time you will have to update it. Simply copy your local debug.keystore file into the repo. On Linux and Mac OS X this is located in ~/.android/. I personally create a new folder named external to keep it separate from the project source code and assets.

Once you have the keystore in your repo, override the default debug signing configuration to use shared file. The sample below are for Gradle, but this is also pretty straightforward in Ant and Maven.

Override Debug Signing Configuration
1
2
3
4
5
6
7
8
9
10
android {

    ...

    signingConfigs {
        debug {
           storeFile file("external/debug.keystore")
       }
   }
}

If you have custom build variants, you can use the debug signing configuration as shown below.

Custom Build Variants
1
2
3
4
5
6
7
8
9
10
android {

  ...

   productFlavors {
       demo {
           signingConfig signingConfigs.debug
       }
   }
}

This is generally one of the first things I do when setting up a project so I don’t have to ever bother with uninstalling and reinstalling an APK manually. For a bit more information on the debug.keystore file, and where it is located on Windows, see the Android Documentation.

ProGuard With Gradle

The official Android developer documentation has a nice overview of why you should use Proguard when building your application. However, that documentation does not yet cover the new Gradle build system.

Android’s Gradle build system has had a built-in task for running ProGuard for a while now. The Android Tools site has a small section about ProGuard, but it misses a few crucial details.

The first step is to enable the ProGuard task on a particular buildType by setting the runProguard flag. By default, ProGuard is disabled on the debug build type. I personally make this explicit, but it is not strictly necessary.

Next, use one of the two default Android files as the basis of your ProGuard configuration. Depending on the age of your Android project, you may have a stale base configuration file. I found the newer files work much better. The safest option is the non-optimized file, I have had trouble with the optimized one in the past.

Base ProGuard files
1
2
3
proguardFile getDefaultProguardFile('proguard-android.txt')
// or
proguardFile getDefaultProguardFile('proguard-android-optimized.txt')

Next add the required ProGuard rules for the project dependencies. I personally prefer to keep each library’s rules in a separate file so that I can re-use the ProGuard configurations between projects easily. The simplest location for these files is in the top level of the project next to the build.gradle file. I have started a collection of ProGuard files for common libraries on GitHub. Using this approach also untangles the ProGuard configuration when removing unused dependencies from a project.

Dependency ProGuard Rules
1
2
proguardFile 'proguard-google-play-services.txt'
proguardFile 'proguard-gson.txt'

Finally, add one ProGuard configuration file that is a catch-all for the truly project specific rules. Common candidates are any classes that require GSON for parsing (or reflection in general). See the ProGuard manual for more detail on actual configuration rules.

Example proguard-project.txt
1
2
-keep class com.kevinrschultz.sample.BuildConfig { *; }
-keep class com.kevinrschultz.sample.model.User { *; }

When you put it all together, a basic ProGuard configuration looks like the code sample below.

Example Gradle ProGuard Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
android {

    ...

    buildTypes {
        debug {
            debuggable true
            runProguard false
        }

        release {
            debuggable false
            runProguard true
            proguardFile 'proguard-bugsense.txt'
            proguardFile 'proguard-eventbus.txt'
            proguardFile 'proguard-google-play-services.txt'
            proguardFile 'proguard-gson.txt'
            proguardFile 'proguard-project.txt'
            proguardFile getDefaultProguardFile('proguard-android.txt')
        }

    }
}

One final note, make sure to keep the mapping.txt file produced by each build to de-obfuscate stack traces. It is located in the build/{appname}/proguard/{buildvariant} folder. You will have to upload the mapping.txt file to your crash reporter (BugSense, Crashlytics, etc). This file changes every time the build is run, so make sure you are careful with versioning the build artifacts (APK & mapping.txt).