SyncAdapter Synchronization Mechanism

Keywords: Android xml encoding Mobile

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

  1. When configuring syncadapter.xml, a ContentProvider privilege was assigned;
  2. 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;
  3. 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" />

Posted by unkwntech on Mon, 17 Jun 2019 09:10:05 -0700