Building a Sync Adapter and Using it on Android

There are numerous of ways that we periodically can sync local data with a backend server, without opening up the app. Some ways are better than others, but today id like to take some time to talk about Androids preferred method of doing this. Using the sync adapter framework.

The implementation it self can be frightening, and lengthy, making developers look for other ways to do the syncing instead. While people would also suggest using the Android AlarmManager, and though it may work, it is not optimized for this kind of operation.

As Android developers, we must understand that the Android OS has very limited resources, and as mentioned in the google docs

A poorly designed alarm can cause battery drain and put a significant load on servers.

Before we move on i need to mention that, a sync adapter should only be used for operations that will most likely occur when the app is NOT open or when the device is sleeping. Otherwise, feel free to use any other method out there, such as using RxJava, the Alarm Manager, a Handler with a timer and thread, IntentService, or even an Async Task….

Alright i was kidding about the async task part.


So what are sync adapters anyways?

For those of you who have never heard of the Sync Adapters, a sync adapter is a method to sync your local mobile data, with a backend server, while taking advantage of many out of the box features that Android gives us by using the sync adapter framework.

Some advantages are:

Alright then, now that we are all exited about this, lets see some code!

So remember when i said that “The implementation it self can be frightening, and lengthy, making developers look for other ways to do the syncing instead.”? well, if you don’t, scroll up and read it again, pay attention this time, if you do, then well…this is the beginning of it.

In order to create a sync adapter we need to first start with an Authenticator.

The sync adapter framework assumes that your sync adapter transfers data between device storage associated with an account and server storage that requires login access. For this reason, the framework expects you to provide a component called an authenticator as part of your sync adapter. This component plugs into the Android accounts and authentication framework and provides a standard interface for handling user credentials such as login information.

For this tutorial, lets assume that we do not require any authentication. This is what a stubbed out authenticator would look like (purposely added the JavaDocs to understand what each one of these methods do.)

Remember, this is a stub, no need to actually add any logic/implementation in any of these. Though, they do seem pretty handy.

Now that we have our authenticator, we need to bind it to a service, so that later on our sync adapter can access the authenticator and do authenticator stuff. This allows an Android binder object to call the authenticator and pass in data between the authenticator and the sync adapter framework.

Since the framework starts this Service the first time it needs to access the authenticator, you can also use the service to instantiate the authenticator, by calling the authenticator constructor in the Service.onCreate() method of the service.

Heres what the service looks like

Note the IBinder method.

Now that we have our authenticator all set, stubbed out, and ready we can add all the metadata that comes along with it. So inside our res/xml directory, we create a new file called authenticator.xml

this file looks something like this

To figure out what each of these fields are you can go HERE but the 2 important fields are accountType, and label. These 2 fields can be any text, just need to make sure that accountType is in the form of a url as you can see. Also, the url doesnt really have to be a valid one.

Next declare the Authenticator in the manifest.

The <intent-filter> element sets up a filter that’s triggered by the intent action android.accounts.AccountAuthenticator, which sent by the system to run the authenticator. When the filter is triggered, the system starts AuthenticatorService, the bound Service you have provided to wrap the authenticator.

The <meta-data> element declares the metadata for the authenticator. The android:name attribute links the meta-data to the authentication framework. The android:resource element specifies the name of the authenticator metadata file you created previously.

Another thing that the Sync Adapter requires is a Content Provider even if you do not need/use one.

The sync adapter framework is designed to work with device data managed by the flexible and highly secure content provider framework. For this reason, the sync adapter framework expects that an app that uses the framework has already defined a content provider for its local data. If the sync adapter framework tries to run your sync adapter, and your app doesn’t have a content provider, your sync adapter crashes.

Well then…i guess we need a content provider.

Thats one hell of a stub there.

Just like any other content provider, we must declare it in the manifest as well. Inside the application tag.

Note the syncable flag. This indicates that the provider allows the sync adapter to make data transfers with it, but only if explicitly done so.

Now, off to the good stuff. The Sync Adapter it self!

The adapter itself is really simple. Like really really simple. Check this out.

Your sync adapter must implement the AbstractThreadedSyncAdapter, the actual background operations happen inside the onPerformSync method. This method gets called automatically when the syncing is supposed to occur. If you haven’t figured this out by now, the entire sync adapter runs on a background thread, so there is no need to add additional background processing in here.

Even though the sync adapter is specific to your app’s data sync requirements and server connection protocols, you need to make sure that you handle server connection yourself, as in, authenticating, downloading data, uploading data, handling data conflicts, and closing network connections and cleaning up after yourself.

The sync adapter does NOT automatically do the data transfers for you. What it does is that it encapsulates your data transfer code, so that the sync adapter can then run the data transfer in the background without any involvement from your application.

Now that we have a way to handle the background operation, we need to give the sync adapter access to our code/information. We do this by creating another service that passes a special Android binder object from the sync adapter to the framework.

The same way we have bound the previous components to the framework, we need to do the same with the sync adapter.

The code is pretty straight forward and self explanatory. The ConfigurationSyncAdapter object is the sync adapter we created previously. The syncAdapterLock is used for thread-safe locking, and the IBinder returns the sync adapters binder to do the actual framework binding mentioned above.

Next, we need to add another file to our res/xml directory called syncadapter.xml

Inside this file we add the following

To know what each of these fields do please take a look at THIS link, though i feel like they are self explanatory.

NOTE: The accountType inside the syncadapter.xml file MUST be the same account type provided in the authenticator.xml file. For this reason i moved the account type to the strings file for easy access whenever needed. Also note the contentAuthority, this will come in handy for later use when using the sync adapter. Put it in the strings file for later use. The contentAuthority can be any string as well. In my case i have it as com.packagename.authority.

Next we need to declare the sync adapter in the manifest of course, along with a could of permissions.

The bold text is what we just added.

Note: The attribute android:process=":sync" tells the system to run the Service in a global shared process named sync. If you have multiple sync adapters in your app they can share this process, which reduces overhead.

And thats it! We are now done with our setup. Now the moment we’ve all been waiting for….running the sync adapter!

You can find additional information about different methods to use for running the sync adapter HERE but i will show you how to run the adapter periodically, and on demand. Keep in mind that you can automatically run a sync adapter when When server data changes, when device data changes, at regular intervals, or on demand.

So for my personal need, in my latest project, i did this by creating a SyncAdapterManager which handles all of the syncing operation for me. Inside my manager, i have a beginPeriodicSync method that runs the sync adapter periodically, when ever i tell it to do so

Here is the setAccountSyncable method

I also have a syncImmediately method as well which can be used to respond to a button click or something

SYNC_EXTRAS_MANUAL Forces a manual sync. The sync adapter framework ignores the existing settings, such as the flag set by setSyncAutomatically().

SYNC_EXTRAS_EXPEDITED Forces the sync to start immediately. If you don’t set this, the system may wait several seconds before running the sync request, because it tries to optimize battery use by scheduling many requests in a short period of time.

To give you a better idea of the full picture, here is my SyncAdapterManager’s full code.

Notice the type, and authority? These are the strings that we used before for accountType, and contentAuthority inside of our authenticator.xml and syncadapter.xml files.

The Account object takes a name and a type. The name should be the same name as specified in our authenticator’s label tag, and the type is the accountType.

Once you run these methods, the Sync adapters onPerformSync method will run, and off you go!

NOTE: When debugging this, if you have any logs inside of your SyncAdapter class make sure that you remove any log filters from Android Studio’s Android monitor to see the logs of your Sync Adapter. If you don’t, you will not see your logs and think your adapter is not running.

The reason for this is that Sync Adapter runs on a Bound Service which is not in the same process, so your logs don’t show in the LogCat under your app main process but in the process that the Sync Adapter is using.

You can use break points to help you debug if you don’t want to use logs.

Another thing you can do is add a filter for the application running the sync adapter by clicking the filter drop down and selecting “Edit filter configuration” option.

This brings up a dialog where you can add specific log filters. Just click the “add/plus” button on the upper left hand corner, and add the apps package name in the Package Name field. This will show the the sync adapter’s log that belong to the application.

 

The full source code can be found in this link.

Thank you for reading. Until next time!

Share On Facebook
Share On Twitter
Share On Google Plus
Share On Linkedin
Share On Pinterest
Share On Reddit
Share On Stumbleupon

One Comment

  1. March 10, 2017
    Reply

    Thank you for this article. There is not too much information on the topic. I use a SyncAdapter myself in one of my projects. Check it out if you are interested. You can find it here: https://bitbucket.org/tbsprs/altglas

Leave a Reply

Your email address will not be published. Required fields are marked *