mdevcon - a primer to syncadapters
TRANSCRIPT
OVERVIEW
v Who is Kiana?
v SyncAdapter Use Cases
v The Big Picture
v Configuration
v Authorization, Content, Synchronization
v Wrap up/Questions
WHO’S KIANA?
v Software Developer
v Professional background working on web portals
v Work at Manheim, wholesale vehicle auction company
v Customer facing applications • Web portal (www.manheim.com) Ruby, Java, JS • iOS app • Android app
SYNC ADAPTERS ARE…?
v Share data between a remote data source and an Android
device
v Android OS provides plug-in architecture that takes care of: • Coordinating network availability with syncing all datasources • Scheduling synchronization/Handling stopped processes • Accommodating user preferences when syncing • Handling network interruptions
TOUGH PARTS
v Good news! Writing a Custom SyncAdapter is actually easy!
v Bad news. There are potentially tough parts: • Authentication Handling • (Custom) Content Provider • Data Handling
v Defined by business rules
BEST USE CASES
Application that…
v Requires authentication step to connect to remote
datasource
v Potentially could use a built-in ContentProvider • All providers found in android.provider package
v Application Suite backed by custom ContentProvider
! (BEST USE CASES)
v Anything that DOES NOT require a login
v Any source that provides an RSS feed • Typically public data sources like news/media sites • Write a parser that reads the feed only when the user wants it • SyncAdapter not necessary to write to the ContentProvider
v If Service does not constantly need to run in the
background • User has limited battery power, code with this in mind
THE BIG PICTURE
AccountManager
• Requires Authentication Service to add Account
• Adds Account to Account List
SyncManager
• Uses Account List as a basis for it’s Sync List
• Requires Sync Service to start data syncs
ContentProvider
• Holds the data • Called in
onPerformSync()
AUTHENTICATION
v Write an AuthActivity • serves UI for user to enter account info • Write it to complete the authorization process
v Write an Authenticator • MUST extend AbstractAccountAuthenticator • Returns the AuthActivity in a Bundle object
v Write an Authentication Service • MUST extend Service • onBind return IBinder for the Authenticator
SYNCHRONIZATION
v Write a SyncAdapter • MUST extend AbstractThreadedSyncAdapter • Overwrite onPerformSync()
v Write a Sync Service • MUST extend Service • Allow creation of ONE SyncAdapter • onBind returns a IBinder for SyncAdapter
RESOURCE XMLS
v Provide configuration info
v Map account type to objects • SyncAdapter and Authenticator are connected via
android:accountType
v Map content authority to SyncAdapter
RESOURCE XMLS
<?xml version="1.0" encoding="utf-8"?>!
<account-authenticator xmlns:android="http://
!schemas.android.com/apk/res/android”!
android:accountType=”your.uniquename.here"!
android:icon="@drawable/icon"!
android:smallIcon="@drawable/icon"!
android:label="@string/label"!
/>!
Add Account
v Notice result of android:icon
v Green lights – Account added
v Gray lights – Account not added
v Clicking one launches Auth
Activity interface
RESOURCE XMLS
<?xml version="1.0" encoding="utf-8"?>!
<sync-adapter xmlns:android="http://
!schemas.android.com/apk/res/android”!
android:contentAuthority="com.android.contacts"!
android:accountType="your.uniquename.here"!
android:supportsUploading="false"!
android:userVisible="true"!
/>!
MANIFEST
v Several appropriate permissions
v Declare AuthenticationService with <service> tag • Intent filter: “android.accounts.AccountAuthenticator” • Meta-data tag points AccountAuthenticator to xml resource file
v Declare SyncService with <service> tag • Intent filter: “android.content.SyncAdapter” • Meta-data tag points SyncAdapter to xml resource file • Other Meta-data tag defines custom MIME-types
v Declare AuthActivity (no Intent filter necessary)
MANIFEST.XML
<service android:name=".authenticator.AuthenticationService” !android:exported="true”>!
<intent-filter>!
<action android:name="android.accounts.AccountAuthenticator" />! </intent-filter>! <meta-data android:name="android.accounts.AccountAuthenticator”!
!android:resource="@xml/authenticator" />!
</service>!<service android:name=".syncadapter.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" />!
<meta-data android:name="android.provider.CONTACTS_STRUCTURE”!!android:resource="@xml/contacts" />!
</service>!
WHY SERVICES?
v SyncAdapter is bound to a service, Authentication is bound to a
service… but why?
v SyncManager finds all syncAdapters via a SyncAdaptersCache.
v AccountManager finds all Accounts via AccountAuthenticatorCache
v RegisteredServicesCache • Reads the XML files created (account-authenticator and syncadapter) • Generates service maps
CODE SUMMARY
v Create a syncadapter.xml file
v Create an authenticator.xml file
v Add an authentication <service> to manifest
v Add a sync <service> to manifest
v Add correct permissions to manifest
v Write some java… but what does any of this mean?
ACCESSING CONTENT
The Device User Android Application(s)
Context
ContentResolver
ContentProviderClient
ContentProvider
Data Storage • SharedPreferences • External Storage • SQLLiteDatabase
CONTENT PROVIDER
v Manages central repository of data.
v SyncAdapters wouldn’t exist without ContentProviders • SyncAdapter mentioned in ContentProvider section of API
Guide
v Built in ContentProviders contain a SyncState table • Use to store sync state, meta-data or any sync related data
v Sync related columns in other tables: • DIRTY, VERSION, SOURCE_ID
IMPORTANT COLUMNS
v DIRTY (local modifications) • Indicates data row has been changed locally since last sync. • When SyncAdapter adds/updates a row, append
“CALLER_IS_SYNCADAPTER” to Content URI
v VERSION • Incremented by Provider whenever the data row is changed. • High-water marking (aka lastSyncState)
v SOURCE_ID • Unique ID for data row (primary key on the remote datasource) • If set to null, indicates to SyncAdapter to create new row on remote
source
CUSTOM CONTENT PROVIDER
v What properties make the built-in ContentProviders
“syncable”? • Extra “For Sync use only” table that holds metadata • “CALLER_IS_SYNCADAPTER” query param • Use of “high-water-mark” is essential
v These properties should be applied to Custom SyncAdapter
v Thread safe
v Many calls to many
ContentAuthorities (CA)
v Expensive call (Many CA lookups)
v Resolver contains a Client
v Not thread safe
v Repeated calls to same CA
v Cheaper call (one CA lookup)
v You MUST call release() to avoid
memory leak
CONTENT RESOLVER VS CONTENTPROVIDER CLIENT
ContentResolver ContentProviderClient
CONTENTPROVIDER CLIENT
v Part of onPerformSync() method signature.
v Not thread safe, so be careful with usage • Utilize locking strategies, ensure no outside services are
performing activities on sync-candidate data
v Android Sample project uses threadsafe ContentResolver
via a Manager
CONTENT RESOLVER
v Do I really need this? Your choice. • ContentResolvers query the ContentProvider • Use it to CRUD data inside syncAdapter.onPerformSync(); • SampleSyncAdapter uses the ContentResolver by way of it’s
ContactsManager class, because it performs batch operations
v Retrieve it using: context.getContentResolver();
v However, there are other ways to access the data
DATA CONSIDERATIONS
v Determine structure of serialized/transmitted data • Using json? xml? yaml? • What does a single row (data unit) look like?
v How does the server parse and save the transmitted data? • Likely defined by business limitations.
v How does the device parse/save the data? • ContentProviderClient or ContentResolver (batch ops)
ACCOUNT
v Account object contains an Account name and Account type. • Note, the Account object does NOT contain a password • Example: user has multiple twitter accounts
v AccountManager maps an Account object to it’s “password”
v Recommended strategy: store Account and authToken on device • SampleSyncAdapter project stores password (possible human error)
v SyncManager can only schedule syncs per Account
ACCOUNT MANAGER
v Contains centralized registry of user’s online accounts
v Used to determine list of data sources to be synced • AccountManager provides list of Accounts • SyncManager grabs the list of Accounts for syncing
v AccountAuthenticatorCache maps accountType to objects
extending AbstractAccountAuthenticator
v Android Component; you don’t write this
AUTHENTICATION
v User chooses to Add an Account • List compiled from array of allowable account types
v System chooses Authenticator based on
“android:accountType” • Binds to a Service which returns the Authenticator’s IBinder
v Authenticator’s overridden addAccount() method is called • Ultimately the Login UI displays, new Account is created
AUTHENTICATOR
v Allows developer to implement authentication • Per remote service
v Must extend AbstractAccountAuthenticator • Responds to an intent “android.accounts.AccountAuthenticator”
advertised by the AccountManager for it’s accountType only.
v Authentication Service returns a Binder to your implemented
AbstractAccountAuthenticator
v addAccount() returns bundle with intent containing Activity
SYNCMANAGER
v Responsible for scheduling, cancelling syncs. • Maintains a list of active syncs (mActiveSyncContext)
v Sync handling strategy very tightly coupled to Accounts • Account is part of most SyncManager methods
v How do the SyncHandler and Account interact? • SyncHandler receives a message containing an
ActiveSyncContext. ActiveSyncContext contains all information about the sync that needs to begin including the Account.
SYNCHRONIZATION
v SyncManager contains ONE SyncHandler
v SyncHandler’s job: receive/handle sync operation messages • On receiving a Connection Message, calls startSync for the
syncAdapter in question • startSync() spins up a new SyncThread • The method onPerformSync() that was implemented is
invoked when run() is invoked on the thread
SYNC ADAPTER
v Where the main party happens. The method onPerformSync()
gets called within a syncThread.run()
v Make use of the DIRTY/VERSION/SOURCE_ID rows
v Instantiate like any class. • However, create only one for thread safety.
ONPERFORMSYNC()
v What should I do in the onPerformSync() method?
v Merge remote data with local data • DIRTY/VERSION/SOURCE_ID columns
v Use business rules to determine order of operations • Whether local data overwrites remote data or vice versa
v Implement a remote connection strategy to access remote
data
WRAP UP
v We learned… • What SyncAdapters are • Set up • Content (ContentProvider/data characteristics) • Authorization (Accounts, Account Management) • Synchronization (SyncManager, SyncService) • Debugging/Dev Tools