Tuesday, May 3, 2011

The revenge of the SyncAdapter: Synchronizing data in Android

Introduction
Most of the applications that are interesting in some way are connected to live data on the Internet. A common scenario is then how to keep local and remote content synchronized. Android does provide an easy and straightforward mechanism to achieve this synchronization: the SyncAdapter.

What is a SyncAdapter?
Stop anything you are doing right now and watch this excellent video by Virgil Dobjanschi. If you are in a hurry forward to 44' when Virgil introduces the third pattern.


To sum up Virgil's words: a SyncAdapter is just a service that is started by the sync manager, which in turn maintains a queue of SyncAdapters. You delegate the responsibility for choosing when to sync to the system, who knows better than you what's going on and which other applications are synchronizing data. The sync manager does also schedule resyncs according to the errors reported by the SyncAdapter, making your code cleaner and eliminating the need of alarms.

How does this work?
The basic idea is to mirror the remote data in a local database and access it through a ContentProvider. You then access this local ContentProvider from you app. You fetch data from it and impact any changes on it.
The SyncAdapter will be in charge of making the remote content match your local data. It will fetch new data and push the local changes to the server.

Writing a ContentProvider
Writing a ContentProvider is outside the scope of this entry, so go read it anywhere else, like here, here or here. There are some other players that we need to take care of.

Adding an account
You need to add an account in order to use a SyncAdapter. This account will appear in the Accounts & sync section of the settings application. From this section the user is able to add, modify or remove his account, as well as choosing what content to synchronize. Your application will also play fairly when the user enables automatic synchronization.
The AccountManager is in charge of managing user credentials. The user enters his credentials just once and all the applications that have the USE_CREDENTIALS permission can query the manager to obtain an authentication token.
In order to add an account to your application you have to extend the AbstractAccountAuthenticator class. It is okay to return null values from the methods you are not going to use. The important ones are addAcount and getAuthToken.

@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType, String authTokenType,
String[] requiredFeatures, Bundle options)
throws NetworkErrorException {

final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
intent.putExtra(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE,
authTokenType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
You have probably noticed that there is a reference to an activity named AuthenticatorActivity. This is just an activity that asks for the user credentials. It will be prompted when the user adds a new account from settings. You can of course use the same activity than when your application is launched.
The other important method to override is getAuthToken. You will call this method from your SyncAdapter if you need to do any authenticated requests to the server. It will return the same token until it is explicitly invalidated in the account manager. Here is where the logic to get an auth token from the server will be placed.

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle options)
throws NetworkErrorException {
if (!authTokenType.equals(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ERROR_MESSAGE,
"invalid authTokenType");
return result;
}
final AccountManager am = AccountManager.get(mContext);
final String password = am.getPassword(account);
if (password != null) {
boolean verified = callSomeLoginServiceThatReturnsTrueIfValid(
account.name, password);

if (verified) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE,
AuthenticatorActivity.PARAM_ACCOUNT_TYPE);
result.putString(AccountManager.KEY_AUTHTOKEN,
hereGoesTheReceivedToken);
return result;
}
}
// Password is missing or incorrect. Start the activity to add the missing data.
final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
// ...
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}

Android provides a handy class called AccountAuthenticatorActivity that has a method to set the authentication result back to the account authenticator. Here is the relevant code of our AuthenticatorActivity that adds the account and sets the result.
private void finishLogin(String token) {
final Account account = new Account(mUsername, ACCOUNT_TYPE);
if (mRequestNewAccount) {
mAccountManager.addAccountExplicitly(account, mPassword, null);
// Extension point. Here we will set up the auto sync for different
// services
// Example: ContentResolver.setSyncAutomatically(account,
// ContactsContract.AUTHORITY, true);
} else {
mAccountManager.setPassword(account, mPassword);
}
final Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mUsername);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
if (token != null && token.equals(AUTHTOKEN_TYPE)) {
intent.putExtra(AccountManager.KEY_AUTHTOKEN, token);
}
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
finish();
}

There is still one step remaining. Android does not call our implementation of the AbstractAccountAuthenticator directly. It must be wrapped in a service that returns a subclass of AbstractAccountAuthenticator from its onBind method.
public class AuthenticationService extends Service {
private static final String TAG = "AuthenticationService";
private Authenticator mAuthenticator;

@Override
public void onCreate() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Authentication Service started.");
}

mAuthenticator = new Authenticator(this);
}

@Override
public void onDestroy() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Authentication Service stopped.");
}
}

@Override
public IBinder onBind(Intent intent) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getBinder() ... returning AccountAuthenticator binder");
}

return mAuthenticator.getIBinder();
}
}

Finally, register the service in the AndroidManifest.xml file and filter the action named android.accounts.AccountAuthenticator. The permissions required for account management are GET_ACCOUNTS, USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS.

Writing the SyncAdapter
First of all throw a file in the res/xml folder that describes the SyncAdapter and what kind of content it is going to synchronize.
<sync-adapter   xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.nasatrainedmonkeys.app.providers.SomeContentProvider"
android:accountType="com.nasatrainedmonkeys.account" />

In order to write a SyncAdapter you have to extend the AbstractThreadedSyncAdapter class and override its onPerformSync method. This method will be run in a newly spawned thread when no other sync operation is running. One of the parameters is an instance of the SyncResult class which is used to inform the SyncManager about any errors in the sync process. With this result the SyncManager can decide whether to reschedule the sync in the future or not.
public class SampleSyncAdapter extends AbstractThreadedSyncAdapter {
private AccountManager mAccountManager;
private ContentResolver mContentResolver;

public SampleSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);

mAccountManager = AccountManager.get(context);
mContentResolver = context.getContentResolver();
}

@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {

String authtoken = null;
try {
authtoken = mAccountManager.blockingGetAuthToken(account,
AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, true);

// Dummy sample. Do whatever you want in this method.
List data = fetchData(authtoken);

syncRemoteDeleted(data);
syncFromServerToLocalStorage(data);
syncDirtyToServer(authtoken, getDirtyList(mContentResolver));
} catch (Exception e) {
handleException(authtoken, e, syncResult);
}
}

private void handleException(String authtoken, Exception e,
SyncResult syncResult) {
if (e instanceof AuthenticatorException) {
syncResult.stats.numParseExceptions++;
Log.e(TAG, "AuthenticatorException", e);
} else if (e instanceof OperationCanceledException) {
Log.e(TAG, "OperationCanceledExcepion", e);
} else if (e instanceof IOException) {
Log.e(TAG, "IOException", e);
syncResult.stats.numIoExceptions++;
} else if (e instanceof AuthenticationException) {
mAccountManager.invalidateAuthToken(
AuthenticatorActivity.PARAM_ACCOUNT_TYPE, authtoken);
// The numAuthExceptions require user intervention and are
// considered hard errors.
// We automatically get a new hash, so let's make SyncManager retry
// automatically.
syncResult.stats.numIoExceptions++;
Log.e(TAG, "AuthenticationException", e);
} else if (e instanceof ParseException) {
syncResult.stats.numParseExceptions++;
Log.e(TAG, "ParseException", e);
} else if (e instanceof JsonParseException) {
syncResult.stats.numParseExceptions++;
Log.e(TAG, "JSONException", e);
}
}

// ...
}

The only missing step is to return the sync adapter from the onBind method of a wrapping service and register the service in the AndroidManifest.xml to filter the intents with action android.content.SyncAdapter.
public class SampleSyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static SampleSyncAdapter sSyncAdapter = null;

@Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SampleSyncAdapter(getApplicationContext(), true);
}
}
}

@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
Conclusion
Use SyncAdapters. They take away the pain of manually managing alarms and awkward synchronization retries. Let the system be responsible for choosing the best time to sync your data and play nicely with the rest of the applications.

References

98 comments:

  1. Another post to crossreference is http://stackoverflow.com/questions/5253858/why-does-contentresolver-requestsync-not-trigger-a-sync/5255360#5255360 -- I explicitly go through all the steps (I think...) to get Sync going.

    ReplyDelete
  2. Cool! I'll add it explicitly to the references as soon as blogger stops breaking the code indentation while I'm editing the post.

    Thanks for all your answers!

    ReplyDelete
  3. Just curious, if we provide the SyncAdapter, we provide the way to stop synchronizing from user. Is this really good idea, to let user stop synchronizing data when we need them up to date in application?

    ReplyDelete
  4. @Marek Sebera:

    Ideally you give the user the chance to enable or disable auto-sync, but you can always force an update and ignore that setting.

    ReplyDelete
  5. Hi, Thank you for the tutorial.
    I have a question. How should I do on the server side? Can I use PHP to check whether the log in username and password are correct?

    ReplyDelete
  6. @Han:

    The server implementation is completely independent from your android application, so you can use any technology you like :)

    ReplyDelete
  7. Adding to Han's question, what is the server side data supposed to look like? Does it have to be data in json or xml? Does the server data have to be restful? From the sample, http://samplesyncadapter2.appspot.com/
    is this a vanilla restful service or what. I'm almost there, have my content provider, have my 'account' and I'm working on the sync adapter implementation. But at some point I need to understand what the server side requirements are.

    ReplyDelete
  8. @fedup:

    The mention to RESTful services is because you can easily mirror the complete set of operations in your database (INSERT, UPDATE, DELETE) to the webservice using HTTP verbs. But it's definitely not a requirement.

    About the data format: JSON or XML are the most used ones. You should have some specific layer or module in your application that knows how to parse server responses and build a model object from it, so you always deal with Java objects regardless of the data format your server is using.

    The tendency is using JSON everywhere, so you are safe with it :)

    ReplyDelete
  9. Thanks for the really useful post! Especially thanks for the link to J.C. Wenger's answers on Stack Overflow. They are the best resource I found on this topic.

    ReplyDelete
  10. Another good resource for sync adapters, giving an actual step-by-step guide to build one is here:

    http://udinic.wordpress.com/2013/07/24/write-your-own-android-sync-adapter/

    ReplyDelete
  11. i want to fetch a big list using sync adapter but in the official documentation,A sync adapter doesn't automatically handle conflicts between data on the server and data on the device. So My question is how will sync adapter behave when connection is turned off? Does it pause the synchronization of the data or it will restart sync whenever connection starts again ?
    I want a big list to be uploaded at my server.

    ReplyDelete
  12. This is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.

    Data Science Training in Chennai
    Data science training in bangalore
    Data science online training
    Data science training in pune
    Data science training in kalyan nagar
    selenium training in chennai

    ReplyDelete
  13. I have visited this blog first time and i got a lot of informative data from here which is quiet helpful for me indeed. 
    Blueprism training institute in Chennai

    Blueprism online training

    ReplyDelete
  14. Thanks for posting this info. I just want to let you know that I just check out your site and I find it very interesting and informative. I can't wait to read lots of your posts

    angularjs Training in chennai
    angularjs-Training in pune

    angularjs-Training in chennai

    angularjs Training in chennai

    angularjs-Training in tambaram

    ReplyDelete
  15. Hmm, it seems like your site ate my first comment (it was extremely long) so I guess I’ll just sum it up what I had written and say, I’m thoroughly enjoying your blog. I as well as an aspiring blog writer, but I’m still new to the whole thing. Do you have any recommendations for newbie blog writers? I’d appreciate it.

    AWS Interview Questions And Answers

    AWS Training in Bangalore | Amazon Web Services Training in Bangalore

    AWS Training in Pune | Best Amazon Web Services Training in Pune

    Amazon Web Services Training in Pune | Best AWS Training in Pune

    AWS Online Training | Online AWS Certification Course - Gangboard

    ReplyDelete
  16. Thanks for such a great article here. I was searching for something like this for quite a long time and at last I’ve found it on your blog. It was definitely interesting for me to read about their market situation nowadays. project management courses in chennai | pmp training class in chennai | pmp training fee | project management training certification | project management training in chennai | project management certification online |

    ReplyDelete
  17. Write more; that’s all I have to say. It seems as though you relied on the video to make your point. You know what you’re talking about, why waste your intelligence on just posting videos to your blog when you could be giving us something enlightening to read?
    Check out the best python training in chennai at SLA

    ReplyDelete
  18. It is a great post. Keep sharing such kind of useful information.

    Article submission sites
    Education

    ReplyDelete
  19. Great article, useful info, keep posting useful information.

    Data Science Courses in Bangalore

    ReplyDelete
  20. I am really enjoying reading your well written articles. It looks like you spend a lot of effort and time on your blog. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work.data science course in dubai

    ReplyDelete
  21. I am really enjoying reading your well written articles. It looks like you spend a lot of effort and time on your blog. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work.data science course in dubai

    ReplyDelete
  22. This is a fantastic website and I can not recommend you guys enough.
    data science course malaysia

    ReplyDelete
  23. I like viewing web sites which comprehend the price of delivering the excellent useful resource Python training in pune free of charge. I truly adored reading your posting. Thank you!

    ReplyDelete
  24. I am really enjoying reading your well written articles. It looks like you spend a lot of effort and time on your blog. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work.
    www.technewworld.in
    How to Start A blog 2019
    Eid AL ADHA

    ReplyDelete
  25. I like viewing web sites which comprehend the price of delivering the excellent useful resource free of charge. I truly adored reading your posting. Thank you!
    machine learning course malaysia

    ReplyDelete
  26. If your looking for Online Illinois license plate sticker renewals then you have need to come to the right place.We offer the fastest Illinois license plate sticker renewals in the state.
    big data course

    ReplyDelete
  27. The Information which you provided is very much useful for Agile Training Learners. Thank You for Sharing Valuable Information.google cloud platform training in bangalore

    ReplyDelete
  28. Very useful and information content has been shared out here, Thanks for sharing it.blue prism training in bangalore

    ReplyDelete
  29. The content was very interesting, I like this post. Your explanation way is very attractive and very clear.data science training in bangalore

    ReplyDelete
  30. Good to know about the email list business. I was looking for such a service for a long time o grow my local business but the rates that other companies were offering were not satisfactory. Thanks for sharing the recommendations in this post!Automation Anywhere Training in Bangalore

    ReplyDelete
  31. Enjoyed reading the article above, really explains everything in detail, the article is very interesting and effective. Thank you and good luck…

    Start your journey with Database Developer Training in Bangalore and get hands-on Experience with 100% Placement assistance from experts Trainers @Bangalore Training Academy Located in BTM Layout Bangalore.

    ReplyDelete
  32. Very interesting, good job and thanks for sharing such a good blog.

    Real Time Experts is a leading SAP CRM Training Institutes in Bangalore providing real time and Job oriented SAP CRM Course with real time Expert Trainers who are Working Professionals with 6+ Years of SAP CRM Experience.

    ReplyDelete
  33. Thank you for sharing such a nice post!

    Learn DevOps from the Industry Experts we bridge the gap between the need of the industry. eTechno Soft Solutions provide the Best DevOps Training in Bangalore .

    ReplyDelete
  34. Nice blog, this blog provide the more information. Thank you so much for sharing with us.

    Nice blog, this blog provide the more information. Thank you so much for sharing with us.
    aws Training in Bangalore
    python Training in Bangalore
    hadoop Training in Bangalore
    angular js Training in Bangalore
    bigdata analytics Training in Bangalore
    python Training in Bangalore
    aws Training in Bangalore

    ReplyDelete
  35. I got Very treasured data from your blog.
    I’m happy with the statistics which you provide for me.

    click here formore info.

    ReplyDelete
  36. Very correct statistics furnished, Thanks a lot for sharing such beneficial data.
    katmovies

    ReplyDelete
  37. I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.

    business analytics courses

    data science course in mumbai

    data analytics courses

    data science interview questions

    ReplyDelete
  38. Pretty article! I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing.

    aws training in bangalore
    aws tutorial videos

    ReplyDelete
  39. Thanks for sharing such a great information..Its really nice and informative..

    oracle erp training online

    ReplyDelete
  40. I am inspired with your post writing style & how continuously you describe this topic. After reading your post, azure tutorial thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic.

    ReplyDelete
  41. Thanks for sharing this wonderful message.
    android training institutes in coimbatore

    data science course in coimbatore

    data science training in coimbatore

    python course in coimbatore

    python training institute in coimbatore

    Software Testing Course in Coimbatore

    CCNA Course in Coimbatore

    ReplyDelete
  42. The article is so informative. This is more helpful for our
    Data Science Course in Hyderabad

    ReplyDelete
  43. Really Thanks For Posting Such a Useful and informative article. oracle training in chennai

    ReplyDelete


  44. Nice article and thanks for sharing with us. Its very informative

    Plots in THIMMAPUR


    ReplyDelete

  45. Really, this article is truly one of the best, information shared was valuable and resourceful Very good work thank you.
    tally training in chennai

    hadoop training in chennai

    sap training in chennai

    oracle training in chennai

    angular js training in chennai

    ReplyDelete
  46. https://trainingoraclesoa.blogspot.com/2012/05/oracle-soa-aia-admin-training-inspiring.html?showComment=1618229497528#c6043174978652693574

    ReplyDelete
  47. It's an excellent article!!! Such a piece of wonderful information and I was getting more concept to your blog. Thanks for your great explanations.
    AWS certification course in Chennai

    ReplyDelete
  48. Very Informative article with detailed explanation. Thanks for sharing your work . keep up the good work Angular training in Chennai

    ReplyDelete
  49. The 1tac TC1200 utilizes Cree XML2 LED innovation and has a yield of 1200 Lumens. The life expectancy of the electric lamp is around 100,000 hours of light. The TC1200 additionally has a carefully directed net to guarantee that the splendor is kept up for the duration of the existence of the morning. You may have used the flashlight before, but 1tac TC1200 tactical flashlight is different from others. Check now 1Tac Flashlight Reviews to clear your doubt. These are flashlights intended to pull twofold obligation. In addition to the fact that they function as electric lamps to help you find uncertainty, they also fill in as weapons.

    ReplyDelete
  50. Amazing shared a detailed coding content, really helpful , Thanks !
    Data Science Training in Pune

    ReplyDelete
  51. Finish the Selenium Training in Chennai from Infycle Technologies, the best software training institute in Chennai which is providing professional software courses such as Data Science, Artificial Intelligence, Java, Hadoop, Big Data, Android, and iOS Development, Oracle, etc with 100% hands-on practical training. Dial 7502633633 to get more info and a free demo and to grab the certification for having a peak rise in your career. Get Selenium Course in Chennai | Infycle Technologies

    ReplyDelete
  52. I really enjoyed reading your article. I found this as an informative and interesting post, so i think it is very useful and knowledgeable. I would like to thank you for the effort you made in writing this article. Knights Varsity Jacket

    ReplyDelete
  53. Really impressed! Everything is very open and very clear clarification of issues. It contains true facts. Your website is very valuable. Thanks for sharing.
    data analytics course in hyderabad

    ReplyDelete
  54. Casino - DrMCD
    No Deposit Bonus Codes · Starburst · LuckyLand · Platinum 동두천 출장마사지 Link · 순천 출장샵 Luckyland Casino · 안산 출장안마 Starburst. $50 No Deposit Free Chip 천안 출장샵 Bonus · Starburst. Casino Promo Code: STARBONUS · No Deposit Bonus. 김천 출장마사지

    ReplyDelete
  55. This was an extremely wonderful post. Thanks for providing this info. Tom Holland Uncharted Leather Jacket

    ReplyDelete
  56. PAKKAHOUSE IS AN INDIAN REALESTATE AND ECOMMERCE WEBSITE, THIS SITE ALSO CATERS FOR MANY
    BROKERAGE STOCK SITES AND ALSO EDU AND GOVERNMENT ADVERTISEMENT,충주출장마사지
    제천출장마사지
    청주출장마사지
    제주출장마사지
    서귀포출장마사지
    FOR MORE INFORMATION AND UPDATE ABOUT NEWS PLEASE CLICK AND VISIT THE SITE FOR DAILY UPDATES

    ReplyDelete
  57. The SyncAdapter greatly facilitates data synchronization in Android apps between local and distant sources. Thank you for your valuable post! The video suggestion is an excellent addition.
    Data Analytics Courses in India

    ReplyDelete
  58. This article on Android synchronization and the use of SyncAdapter is highly informative and well-structured. It provides clear explanations, code snippets, and references, making it a valuable resource for developers looking to implement data synchronization in Android applications.
    Data Analytics Courses In Dubai

    ReplyDelete
  59. The explanation that you have given in this blog about android synchronization is wonderful. It was very easy for me to understand every part of this blog.
    Visit - Data Analytics Courses in Delhi

    ReplyDelete
  60. The explanation on SyncAdapter is very informative and insightful thanks for sharing valuable blog post.
    data analyst courses in limerick

    ReplyDelete
  61. Thank you for sharing fantastic tutorial and insights on Synchronizing data in Android.
    Digital Marketing Courses in Italy

    ReplyDelete
  62. These tips are awesome and you had a wonderful product. It is beneficial and very informative and I learned a lot from it. nice and interesting post.
    Data analytics framework

    ReplyDelete