Connecting The Dots with Android SyncAdapter

22 09 2010

I recently had an opportunity to work with the Android SDK. This was my first foray into Android development and I found it interesting and rewarding. One of my tasks was to look into creating a sync adapter, responsible for syncing our local device storage with a RESTful service in the cloud. While a sync example is provided in the SDK and the Android javadocs are fruitful, there was still a certain lack of prescription that I find necessary when looking into a new technology. This blog will provide the necessary steps in a prescriptive format, hoping to help any others struggling with this topic.

Before getting started, I absolutely must recommend viewing the 2010 Google I/O presentation on Android REST Client Applications, presented by Virgil Dobjanschi. It’s a fantastic introduction into some of the concepts and recommended patterns for writing well design syncing applications. Along with this presentation and the sync adapter sample provided with the SDK, the following list should provide you ample ammunition to get a sync adapter up and running fairly quickly.

1. ContentProvider
There are two important Android components that are required for your sync adapter, the basic being a ContentProvider. The content provider should contain data you want to sync. Content providers are generally backed by one of the storage mechanisms in Android: Shared Preferences, Internal Storage, External Storage, SQLite Database or network storage. Writing a content provider is outside the scope of this entry. However, the main thing to note when implementing a content provider is the authority you assign in the Android manifest. It will be used in another step to tie some of our components together. In this example, it’s com.captechventures.unanet seen in this snippet:

<provider android:name=".content.UnanetProvider"
        android:authorities="com.captechventures.unanet" android:enabled="true"></provider>

2. Account
The other important component is that you must have an account registered with the Android OS that you intend to link to your sync adapter. The bad news is that there is no “stock” functionality to give you an easy way to provide an Account to the system. However, in the same Sync Adapter Example that comes with the SDK there is a lot of code you can borrow to give you Account functionality. Unless you desire a custom credentials screen, you can heist all the code in the com.example.android.samplesync.authenticator package with only a few minor changes. You’ll also need to snag the authenticator.xml from the resource directory. There is a reference to Constants.ACCOUNT_TYPE in the Authenticator class. Be sure to replace this value with your own account type value, such as com.captechventures.unanet.account which is what we used for our example. You’ll also need to change the accountType attribute in the authenticator.xml to match the value you just used for replacement.

3. Sync Adapter Descriptor
This is the file that ties your sync adapter to the content provider and the account. In the res/xml folder, you’ll need to place an xml file that describes your sync adapter. It doesn’t matter the name, the contents are what’s important (that was foreign to me). Here is my example, found in the res/xml folder and named syncadapter.xml:

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.captechventures.unanet"
    android:accountType="com.captechventures.unanet.account"
    android:supportsUploading="false"
/>

Recognize the contentAuthority and accountType attributes and their values. This is how we tie our content provider and our account to our sync adapter.

4. Abstract Threaded Sync Adapter Implementation
You’ll need to implement an AbstractThreadedSyncAdapter. Here is where the meat of your sync logic will live/start. There’s no one right way for sync logic and it will be different from implementation to implementation. However, I highly recommend following one of the patterns Dobjanschi outlines in his presentation for managing the sync itself. Once you watch the presentation and absorb the information, it just “makes sense” and doesn’t seem like there could be a better way.

Be sure to use the SyncResult in your overridden onPerformSync method appropriately. Using the object in the appropriate manner will allow the sync manager to be more effective.

5. Sync Adapter Service
The syncing mechanism will run in the background on a different thread from the UI. To do this, we’ll need to setup a service that filters on sync intents. The service implementation is simple and only a few lines long. Follow the example in the Sync Adapter Sample, changing the types to match yours. Here is the declaration in the Android Manifest for my sample service, notice the addition of the intent filter and the android:resource reference to the Sync Adapter Descriptor we created in step 3:

<service android:name=".sync.SyncService"
        android:exported="true">
        <intent-filter>
                <action android:name="android.content.SyncAdapter" />
        </intent-filter>
        <meta-data android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter" />
</service>

6. Android Manifest
The final piece is to pull everything together in your Android Manifest. We’ve already identified you’ll need your content provider as well as the Service responsible for reacting to sync manager requests declared. You’ll also need to provide a few permissions to allow several aspects of your application to function appropriately: ability to read/write accounts, ability to interact with the network, and ability to read/write sync settings. Here is a snippet from our sync adapter:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />

Conclusion
While this roadmap is aimed at kickstarting your sync adapter development, it by no means replaces reading and understanding the Android documentation on sync adapters and all the related classes and components. Be sure to read the documentation linked throughout this entry.

Advertisements




BlackBerry APIs Need Some Work

18 03 2010

While unstaffed the last few weeks, a manager tapped me to help put together a POC BlackBerry application for one of our clients. I have had experience working on a BlackBerry project from a previous employer, however I did not have hands on experience programming the device myself. I was looking forward to this as I had been wanting to do some dabbling with mobile devices, I just had not had the time.

I knew programming for BlackBerries were a bit tricky due to the numerous versions of the OS and devices, each with their own hardware specs (with or without GPS, supported resolution, touchpad, trackball, etc.). We tailored our POC for the 8800 series due to clent request and for the GPS API support. I downloaded the latest java development software, version 5.0 beta. With 11+ years of Java experience, jumping in and immediately putting together an application was easier than anticipated. However, I quickly became frustrated at the APIs (or lack thereof) that has me seriously wondering if RIM doesn’t need to do an overhaul on their APIs and development model for applications.

For example, I had RichTextFields that required a text color other than black. Nothing existed on the class that resembled a way to change the text color so I Googled. I came across several message board posts, but all seemed to point to the same resolution: override the paint(Grahpics) method of the class to change the color on the Graphics object. Say wuh? Just to change the color of the text, I had to override the entire paint method? Here’s an example of what was required:

RichFieldText rft = new RichFieldText("Here's my text") {
        public void paint(Graphics g) {
                g.setColor(Color.Red);
                super.paint(g);
        }
};

Of course, you could provide a base class responsible for these sort of things and make it available to your entire project, but this is a horrible model given the triviality of changing the text color of a text field! I found another example of this poor development model while working with the MapField class. I have to think one of the most common use cases for this component is to “mark” something on the map, once the desired location is within the view. Again, there are no APIs to provide this and to implement this fucntionality, you have to override/extend and draw the bitmap yourself:

MapField mf = new MapField() {
        protected void paint(Graphics graphics) {
                super.paint(graphics);
                XYPoint center = new XYPoint();
                convertWorldToField(getCoordinates(), center);
                Bitmap bitmap = Bitmap.getBitmapResource("house.png");
                graphics.drawBitmap(center.x, center.y, bitmap.getWidth(), bitmap.getHeight(), bitmap, 0, 0);
        }
};

I also found the lack of useable layout managers another source of frustration. It seems unless you want things lined up left to right or up to down, you really don’t have a lot of options unless you want to write something yourself. Keep in mind, I’m using the latest version of their APIs. Hard to believe.

Given the numerous devices (each with their own specifications), the various flavors of the OS, and the seemingly poor development model set forth by RIM, I can’t imagine having to develop and maintain so many code bases for a product. If the architecture and opportunity presented itself, I think I’d rather develop a web application to use the much improved BlackBerry browser or a third-party browser and tailor the view if necessary.