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.

About these ads

Actions

Information

26 responses

27 11 2010
Rishi

Can sync adapters be scheduled similar to services using AlarmManager? I read in Dobjanschi’s presentation that “sync adapters should be used to execute background intervals that are not time critical”. I have been searching for sync adapter implementations for audio, video and photo mediastores on android 2.2 with no luck. Any suggestions regarding this?

27 11 2010
Eric Miles

If you’re using 2.2, take a look at the ContentResolver class. There is an api that you can change the sync interval, however there is no cron like interface to make it absolute.

1 12 2010
Malik

This is a really helpful article just for the startup on the sync adapter. I was just wondering that can you also please put some code samples that show that how we can set the flags on the rows in the database tables that will be used for sync?

19 01 2011
Eric Miles

In our specific application, we strayed from the design pattern slightly as our RESTful services weren’t quite as fine grained as suggested in the Google I/O presentation. We merely had a single column that identified if a change had been made locally, if that flag was set, it was posted during the sync and then that flag flipped so we wouldn’t post it at the next sync.

19 01 2011
himanshu

Can you give a little bit more explanation like who initiate the the sync adapter if it is sync manager how it does that and what make it to do that. Is the sync adapter run periodically or when it gets notification that raw-contacts has been changed ?, can we customize the sync adapter execution ??. If you have any idea please shed light on that.

thanks

19 01 2011
Eric Miles

If you’re targeting 2.2+, there is an API on the ContentResolver class that allows you to schedule your syncs.

http://developer.android.com/reference/android/content/ContentResolver.html#addPeriodicSync(android.accounts.Account, java.lang.String, android.os.Bundle, long)

You can also request a sync programmatically:

http://developer.android.com/reference/android/content/ContentResolver.html#requestSync(android.accounts.Account, java.lang.String, android.os.Bundle)

You could setup a ContentObserver for notifications to raw-contacts and then request a sync based off the receipt of a change…

19 01 2011
himansu

@Eric,
Thanks for prompt reply..
I have couple of questions
1) Can we use syncAdapter to get data of a row of raw_contact table if that row has been affected (i.e added/deleted/edited). I know ContentObserver notify whenever raw_content table changes in onChange() method but we dont know which row has been changed ( to put this in another way if we add we can know through the query highest id is inserted and if deleted we still get it through deleted flag in raw_contact table but how about edited one??).

2) when we get notification in ContentObserver we can fire requestSync (Account account, String authority, Bundle extras) but in onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) we have provider and SyncResult .. how we can get data of raw_contact affected by contact application using these to parameters .

thanks again man !! your blog is really nice please share some idea what you think .

9 03 2011
Maximiliano

Hello Eric, this is a great post, very clarifying, but I have a question about the fourth step “Abstract Threaded Sync Adapter Implementation”, there you mentioned that “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”. I thought that those patterns involves all the work to do, not just a step in the development, is that correct?
Thanks in advance, regards.

9 03 2011
Eric Miles

You are correct, it will be the bulk of the work you do. Regarding the patterns, Dobjanschi in his presentation discusses sync bits, in syncing bits, local change bits, etc on a record in your local data store. These are the patterns I recommend following or a form of them that fits your specific needs. For instance, our services weren’t fine grained enough to warrant each and every bit he recommended (nor was our UI mature enough to deal with all that metadata). So we simply had a bit to identify if the record was local, in transit, or synced.

9 03 2011
Maximiliano

Thanks a lot for the reply, and again, great post

14 03 2011
Mark

Very nice post. Thanks for putting this together. I have one question: If I just use the existing Google account on the phone (meaning account type of com.google) to sync my application, then i don’t really need any code like the authenticator.xml or anything in similar to what’s in com.example.android.samplesync.authenticator, right? Anything else I need to be aware of in that case?

22 07 2011
Eric Miles

You are correct, you don’t need all the authenticator stuff if you’re leveraging an existing account. However, in order to use an existing account, your application must be configured to use that account, must be configured to use the same process, and must be signed with the same certificate (assuming the account/authentication mechanism resides in another application).

13 10 2011
Eric Miles

I was mistaken in my previous reply. You can not use the account/credential information from another authenticator unless you both use the same process ID and are signed with the same certificate. So no, you can NOT use the existing Google account. Please see my recent blog post on this exact subject.

http://ericmiles.wordpress.com/2011/09/23/android-single-account-multiple-application-prescription/

17 08 2011
jameswonlife

Thank you so much for sharing this.

23 09 2011
Android Single Account, Multiple Application Prescription « Tight Lines and Tight Deadlines

[...] for this prescription, but you’ll need to create the authentication mechanism. Consult a previous blog I have on this subject for that [...]

11 10 2011
indela sreenivasulu (@indelasreeni)

hii

i got code simplesyncadapter. but its is not running please send me the zip file to mail emai
it is grate help to me.
thanks in andvance….
email:indela447@gmail.com

13 10 2011
thiranjith

Thanks for the post. It was very helpful for expanding my understanding on custom syncing. Have you played around with turning the Auto-Sync functionality ON/OFF programatically? ContentProvider appears to have some functionality related to this, but (a) it seems to be doing it on one account at a time, (b) not practical since an external app wouldnt have access to other Accounts’ auth_tokens (e.g. Google Accounts, FB accounts etc.).

Any suggestions on going about achieving this? Thanks!

13 10 2011
thiranjith

Ah! Found that ContentResolver#setMasterSyncAutomatically accomplishes this to some extent!

21 12 2011
Julian

Hi I’m green with Android SDK.
I have read thru all yours above. I want to write an app which allow view content which is update by wordpress using XML or JSON.
Is it the same thing, tools I should use?

21 05 2012
JohnHaze

The Bundle argument named “extras” of the method ContentResolver.requestSync is NOT allowed to contain a Parcelable object

I would like to send a custom Parcelable object to my SyncAdapter. The method ContentResolver.validateSyncExtrasBundle does not allow me to do that. This validation method is called by the ContentResolver.requestSync method. To me this seems to be quite illogical, because a Bundle is meant to be able to contain Parcelable objects.

Are there any other elegant ways to do that, except using static fields?

Thx in advance

21 05 2012
Eric Miles

The problem here is that you need this data to be persisted across your process being stopped, assuming Android could stop your process after the sync request is made but before it’s processed by your sync adapter. So statics/singletons will not work. I would use persistent storage of some sort: Shared Pref if possible or a SQLite DB.

I don’t know what their rationalization for only allowing primitives and the Account class as Extra types to the bundle for sync requests. Only thing I can think of is that this is data the SyncManager has to manage and they’re trying to reduce the data footprint of that service…

12 09 2012
Harry

Hi Eric, thanks for this post.

I have the above pretty much setup. However now I want to leverage the use of AuthenticatorActivity when my app starts to authenticate a user and add them to Account & Sync if not already there. This is possible however when the user is authenticated it forwards the activity to the Accounts & Sync section. How can I stop this so I can move a user to the next step in my app? Thanks for your/any help.

2 10 2012
locksmith columbia md

What’s up, all is going well here and ofcourse every one is sharing facts, that’s
actually fine, keep up writing.

23 01 2013
22 05 2013
dibakar

Hi
Thanks for summing it up so nicely. I am new to android and I strugling to sync my local Sqlite db with a SqlServer DB. So I understand I need to develop a ContentProvider which will fetch all dirtydata (for me all newly created data) and need to be used inside onPerformSync of SyncAdapter. However I dont see any call to ContentResolver in the sample code. Am i missing something or it is available inside the AccountManager Package. Thanks

22 07 2013
Own sync adapter for Android? - Android Questions - Developers Q & A

[…] article, http://ericmiles.wordpress.com/2010/09/22/connecting-the-dots-with-android-syncadapter/, is a great, albeit brief overview of creating a sync adapter and getting it to play nice within […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow

Get every new post delivered to your Inbox.

%d bloggers like this: