Official documents: https://developer.android.com/training/sync-adapters/index.html
1. Introduction
- Facebook can regularly update your friends with the latest information and integrate updates and mood phrases into your contacts.
- Purpose 2
- Purpose 3
2. Synchronization Framework Structure
3. Account Management
AuthenticationService class
- AuthenticationService is a service that inherits a Service to provide cross-process calls for system synchronization framework calls.
- Fixed Action s are android.accounts.AccountAuthenticator
Here is the registration in manifest:
<service android:name=".AuthenticatorService" android:enabled="true" 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>
Corresponding services:
public class AuthenticatorService extends Service {
//mAuthenticator is intended for account authentication
private Authenticator mAuthenticator;
public AuthenticatorService() {
}
@Override
public void onCreate() {
super.onCreate();
mAuthenticator = new Authenticator(this);
}
@Override
public IBinder onBind(Intent intent) {
//The primary mAuthenticator
return mAuthenticator.getIBinder();
}
}
Authenticator class
Authenticator is a class inherited from AbstractAccountAuthenticator. AbstractAccountAuthenticator is a virtual class that defines the basic interface for handling the functions of adding, deleting and verifying Accounts in Accounts and Synchronization in Mobile Settings and implements some basic functions.
AbstractAccountAuthenticator has an internal class inherited from IAccountAuthenticator.Stub that wraps remote interface calls to AbstractAccountAuthenticator.We can use the getIBinder () method of AbstractAccountAuthenticator to return the IBinder form of an internal class so that we can make remote calls to this class, such as those in the onBind method of the code above.
authenticator.xml
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.crazyman.accountsyncdemo.type"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:smallIcon="@mipmap/ic_launcher" />
4. Synchronization Management
The use of sync mechanism is similar to account management and is also a cross-process communication based on binder mechanism.First it needs a Service that provides an Action to the system so that it can find it; then it inherits and implements AbstractThreadedSyncAdapte r, which contains an internal class that implements ISyncAdapter.Stub, which encapsulates remote interface calls, the getSyncAdapterBinder() method that returns the IBinder form of the internal class so that it can read AbstractThreaderEdSyncAdapte makes remote calls; the Service registration is required in the manifest and meta-data is specified, which is an XML file named syncadapter.xml in the SamplleSyncAdapter instance, which specifies the account and the listened contentprovider.These files are described below:
SyncService
public class SyncService extends Service {
private static final String TAG = "SyncService";
private static final Object sSyncAdapterLock = new Object();
private static SyncAdapter sSyncAdapter = null;
/**
* Thread-safe constructor, creates static {@link SyncAdapter} instance.
*/
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
@Override
/**
* Logging-only destructor.
*/
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service destroyed");
}
/**
* Return Binder handle for IPC communication with {@link SyncAdapter}.
*
* <p>New sync requests will be sent directly to the SyncAdapter using this channel.
*
* @param intent Calling intent
* @return Binder handle for {@link SyncAdapter}
*/
@Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
- SyncService is a service that inherits a common Service to serve remote processes and returns IBinder in the onBind method.
Manifest.xml is registered as follows:
<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" />
</service>
- An adapter can only synchronize one Authority. To synchronize multiple Authority with one account, you can register multiple sync-adapter s with the system that bind the same account.
syncadapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.crazyman.accountsyncdemo.type"
android:allowParallelSyncs="false"
android:contentAuthority="com.crazyman.accountsyncdemo.provider"
android:isAlwaysSyncable="true"
android:supportsUploading="false"
android:userVisible="false" />
The syncadapter.xml file specifies the Authority of the contentprovider that this Service listens on, and the account type accountType that listens on this Authority.Common properties and functions are as follows:
attribute | describe |
---|---|
android:contentAuthority | Specify the permissions required for the ContentProvider to be synchronized |
android:accountType | Indicates the type of account being synchronized |
android:userVisible | Set whether to show in Settings - Accounts, when set to true, the user can manually turn off synchronization |
android:supportsUploading | Set whether notifyChange notifications are required to synchronize |
android:allowParallelSyncs | Whether concurrent synchronization is supported |
android:isAlwaysSyncable | Default is false, when true means isSyncable is set to 1 for all accounts |
SyncAdapter.java
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = SyncAdapter.class.getSimpleName();
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.d(TAG, "SyncAdapter onPerformSync : Sync Data Start");
// TODO: 2007/7/1 for specific data synchronization
Log.d(TAG, "SyncAdapter onPerformSync : End of Synchronization Data");
}
}
- Within the AbstractThreadedSyncAdapter, two methods, startSync() and cancelSync(), are provided, which are mainly called by remote system processes.StartSync () will start a thread in which synchronization is performed by calling the onPerformSync() method of AbstractThreadedSyncAdapter.So the operations in the onPerformSync method above are all performed in a new thread.
- cancelSync() will interrupt the synchronization operation.
5. Operation process of synchronization mechanism
How do clients automatically synchronize
- When configuring syncadapter.xml, a ContentProvider privilege was assigned;
- Call ContentResolver.notifyChange(Android.net.Uri,android.database.ContentObserver, boolean) at the client to notify us when the data for the ContentProvider corresponding to this privilege changes;
- ContentResolver.notifyChange->getContentService().notifyChange()->ContentService.notifyChange();
The following code to look at ContentService.notifyChange() is:
@Override
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, int flags,
int userHandle) {
try {
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
synchronized (mRootNode) {
mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
flags, userHandle, calls);
}
final int numCalls = calls.size();
for (int i=0; i<numCalls; i++) {
ObserverCall oc = calls.get(i);
try {
oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
+ uri);
} catch (RemoteException ex) {
synchronized (mRootNode) {
Log.w(TAG, "Found dead observer, removing");
IBinder binder = oc.mObserver.asBinder();
final ArrayList<ObserverNode.ObserverEntry> list
= oc.mNode.mObservers;
int numList = list.size();
for (int j=0; j<numList; j++) {
ObserverNode.ObserverEntry oe = list.get(j);
if (oe.observer.asBinder() == binder) {
list.remove(j);
j--;
numList--;
}
}
}
}
}
if ((flags& ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
uri.getAuthority());
}
}
synchronized (mCache) {
final String providerPackageName = getProviderPackageName(uri);
invalidateCacheLocked(userHandle, providerPackageName, uri);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
There are two things you did above:
1. Notify all interested Observer s of the ContentProvider;
2. Notify the syncadapter registered by SyncManager that is interested in the URI.
6. Permission Settings
The following permissions are generally required
<!--Normally synchronizing data requires a network connection--> <uses-permission android:name="android.permission.INTERNET" /> <!--Synchronization framework functions need read and write permissions and account settings permissions as follows--> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />