Создание адаптера синхронизации

Компонент адаптера синхронизация в вашем приложении инкапсулирует код для задач, которые передают данные между устройством и сервером. На основе планирования и запуска из вашего приложения, платформа адаптеров синхронизации запускает код компонента адаптера синхронизации. Чтобы добавить компонент адаптера синхронизации для вашего приложения, вам необходимо добавить следующие компоненты:

Класс адаптера синхронизация.
Класс, который оборачивает ваш код передачи данных в интерфейсе, совместимым с платформой синхронизации.
Связанный Service.
Компонент, который позволяет платформе синхронизации запускать код класса адаптера синхронизации.
XML файл метаданных адаптера синхронизации.
Файл, содержащий информация об адаптере синхронизации. Платформа читает этот файл, чтобы узнать, как загрузить и запланировать передачу данных.
Объявления в манифесте приложения.
XML, который объявляет связанный сервис и указывает на метаданные адаптера синхронизации.

Этот урок покажет вам, как определить эти элементы.

Создание класса адаптера синхронизации

В этой части урока вы узнаете, как создать класс адаптера синхронизации, который инкапсулирует код передачи данных. Создание класса включает расширение базового класса адаптера синхронизации, определение конструкторов класса, а также реализация методов, в которых вы определите задачи передачи данных.

Расширение базового класса адаптера синхронизации AbstractThreadedSyncAdapter

Чтобы создать компонент адаптера синхронизации, начните с расширения AbstractThreadedSyncAdapter и создания его конструкторов. Используйте конструкторы для выполнения задач по настройке каждый раз, когда компонент адаптера синхронизация создается с нуля, так же как вы используете Activity.onCreate() для настройки деятельности. Например, если ваше приложение использует поставщик контента для хранения данных, используйте конструкторы для получения экземпляра ContentResolver . Поскольку вторая форма конструктора была добавлена в Android платформу версии 3.0 для поддержки parallelSyncs аргумента, вам нужно создать две формы конструктора для сохранения совместимости.

Примечание: Платформа синхронизации разработана для работы с компонентами адаптеров синхронизации, которые являются "одиночками". Создание экземпляра компонента адаптера синхронизации рассматривается более подробно в разделе Свяжите адаптер синхронизации с платформой.

В следующем примере показано, как реализовать AbstractThreadedSyncAdapterи его конструкторы:

/**
 * Handle the transfer of data between a server and an
 * app, using the Android sync adapter framework.
 */
public class SyncAdapter extends AbstractThreadedSyncAdapter {
    ...
    // Global variables
    // Define a variable to contain a content resolver instance
    ContentResolver mContentResolver;
    /**
     * Set up the sync adapter
     */
    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        mContentResolver = context.getContentResolver();
    }
    ...
    /**
     * Set up the sync adapter. This form of the
     * constructor maintains compatibility with Android 3.0
     * and later platform versions
     */
    public SyncAdapter(
            Context context,
            boolean autoInitialize,
            boolean allowParallelSyncs) {
        super(context, autoInitialize, allowParallelSyncs);
        /*
         * If your app uses a content resolver, get an instance of it
         * from the incoming Context
         */
        mContentResolver = context.getContentResolver();
        ...
    }

Добавьте код передачи данных в onPerformSync()

Компонент адаптера синхронизация не выполняет передачу данных автоматически. Вместо этого, он инкапсулирует код передачи данных, так что платформа может запустить передачу данных в фоновом режиме, без участия вашего приложения. Когда платформа готова синхронизировать данные вашего приложения, она вызывает реализацию вашего метода onPerformSync().

Для облегчения передачи данных из кода вашего основного приложения в компонент адаптера синхронизации, в платформа синхронизации вызывает onPerformSync() со следующими аргументами:

Account
Account объект, связанный с событием, которое вызывается адаптером синхронизации. Если ваш сервер не использует учетные записи, вам не нужно использовать информацию из этого объекта.
Extras
Bundle содержащий флаги, посланные событием, которое вызвал адаптер синхронизации.
Authority
Авторитетный источник поставщика контента в системе. Ваше приложение должно иметь доступ к этому поставщику контента. Как правило, авторитетный источник соответствует поставщику контента вашего собственного приложения.
Клиент поставщика контента
ContentProviderClient для провайдера контента, на который указывает аргумент authority. ContentProviderClient это легкий открытый интерфейс поставщика контента. Он имеет те же функциональные возможности, что ContentResolver. Если вы используете поставщик контента для хранения данных вашего приложения, вы можете подключиться к провайдеру используя этот объект. В противном случае, вы можете его игнорировать.
Результат синхронизации
SyncResult объект, который вы используете для отправки информации через платформу синхронизации.

Следующий фрагмент показывает общую структуру onPerformSync():

    /*
     * Specify the code you want to run in the sync adapter. The entire
     * sync adapter runs in a background thread, so you don't have to set
     * up your own background processing.
     */
    @Override
    public void onPerformSync(
            Account account,
            Bundle extras,
            String authority,
            ContentProviderClient provider,
            SyncResult syncResult) {
    /*
     * Put the data transfer code here.
     */
    ...
    }

В то время как фактическая реализация onPerformSync() специфична для синхронизации данных вашего приложения и протокола подключения к серверу, есть несколько общих задач, которые должна выполнять ваша реализация :

Подключение к серверу
Хотя можно предположить, что сеть доступна, когда вы начинаете передачу ваших данных, платформа синхронизации не подключается к серверу автоматически.
Загрузка и выгрузка данных
Адаптер синхронизации не автоматизирует задачи передачи данных. Если вы хотите скачать данные с сервера и сохранить их используя поставщик контента, вы должны предоставить код, который запрашивает данные, загружает их, и сохраняет. Аналогичным образом, если вы хотите отправить данные на сервер, вы должны прочитать их из файла, базы данных или поставщика, а также отправлять необходимый запрос для загрузки. Вы также должны обрабатывать сетевые ошибки, возникающие во время передачи данных.
Обработка конфликтов данных или определении актуальных данных
Адаптер синхронизации не обрабатывает конфликты автоматически между данными на сервере и данными на устройстве. Кроме того, он не может автоматически определить, являются ли данные на сервере новее, чем данные на устройстве, или наоборот. Вместо этого, вы должны предоставить свои собственные алгоритмы для обработки этих ситуаций.
Очистка.
Всегда закрывайте подключения к серверу и очищайте временные файлы и кэш в конце вашей передачи данных.

Примечание: Платформа синхронизации выполняет onPerformSync() в фоновом потоке, так что вам не придется создавать свою собственную фоновую обработку.

В дополнение к вашим задачам синхронизации, вы должны попытаться объединить регулярные сетевые задачи и добавить их в onPerformSync(). Сконцентрировав все сетевые задачи в этом методе, вы сохраните энергию батареи, которая нужна для запуска и остановки сетевых интерфейсов. Чтобы узнать больше о том, как сделать доступ к сети более эффективным, см. учебный курс Передача данных без разрядки аккумулятора, который описывает несколько подходов доступа к сети, которые вы можете включить в коде для передачи данных.

Свяжите адаптер синхронизации с платформой

Теперь у вас есть код передачи ваших данных, инкапсулированный в компонент адаптера синхронизации, но вы должны предоставить платформе доступ к коду. Для этого вам нужно создать Service , который передает специальный связующий объект компонента адаптера синхронизации Android платформе. Используя связующий объект, платформа может вызывать onPerformSync() метод и передать данные в него.

Создайте экземпляр вашего компонента адаптера синхронизации как "одиночку" в onCreate() методе сервиса. Создавая экземпляр компонента в onCreate(), вы откладываете его создание пока не запустится служба, которая запускается, когда платформа первый раз пытается выполнить передачу данных. Вам нужно создать экземпляр компонента в поточно-безопасным образом, поскольку платформа синхронизация ставит в очередь выполнение методов адаптера синхронизации в ответ на срабатывания или планирование.

Например, следующий фрагмент показывает, как создать класс, реализующий Service, создает экземпляр вашего компонента адаптера синхронизации, и получает связующий объект Android:

package com.example.android.syncadapter;
/**
 * Define a Service that returns an IBinder for the
 * sync adapter class, allowing the sync adapter framework to call
 * onPerformSync().
 */
public class SyncService extends Service {
    // Storage for an instance of the sync adapter
    private static SyncAdapter sSyncAdapter = null;
    // Object to use as a thread-safe lock
    private static final Object sSyncAdapterLock = new Object();
    /*
     * Instantiate the sync adapter object.
     */
    @Override
    public void onCreate() {
        /*
         * Create the sync adapter as a singleton.
         * Set the sync adapter as syncable
         * Disallow parallel syncs
         */
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
            }
        }
    }
    /**
     * Return an object that allows the system to invoke
     * the sync adapter.
     *
     */
    @Override
    public IBinder onBind(Intent intent) {
        /*
         * Get the object that allows external processes
         * to call onPerformSync(). The object is created
         * in the base class code when the SyncAdapter
         * constructors call super()
         */
        return sSyncAdapter.getSyncAdapterBinder();
    }
}

Примечание: Чтобы увидеть более подробный пример сервиса для адаптера синхронизации, см. пример приложения.

Добавить аккаунт необходимый платформой

Платформа синхронизации требует от каждого адаптера синхронизации иметь тип учетной записи. Вы объявили значение типа учетной записи в разделе Добавление аутентификатора в файл метаданных. Теперь вы должны настроить этот тип учетной записи в Android системе. Для настройки типа учетной записи, добавьте фиктивные учетные записи, которые использует данный тип учетной записи, вызвав addAccountExplicitly().

Лучшее место для вызова метода находится в onCreate() методе, открывающейся деятельности вашего приложения. Следующий фрагмент кода показывает, как это сделать:

public class MainActivity extends FragmentActivity {
    ...
    ...
    // Constants
    // The authority for the sync adapter's content provider
    public static final String AUTHORITY = "com.example.android.datasync.provider"
    // An account type, in the form of a domain name
    public static final String ACCOUNT_TYPE = "example.com";
    // The account name
    public static final String ACCOUNT = "dummyaccount";
    // Instance fields
    Account mAccount;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        // Create the dummy account
        mAccount = CreateSyncAccount(this);
        ...
    }
    ...
    /**
     * Create a new dummy account for the sync adapter
     *
     * @param context The application context
     */
    public static Account CreateSyncAccount(Context context) {
        // Create the account type and default account
        Account newAccount = new Account(
                ACCOUNT, ACCOUNT_TYPE);
        // Get an instance of the Android account manager
        AccountManager accountManager =
                (AccountManager) context.getSystemService(
                        ACCOUNT_SERVICE);
        /*
         * Add the account and account type, no password or user data
         * If successful, return the Account object, otherwise report an error.
         */
        if (accountManager.addAccountExplicitly(newAccount, null, null))) {
            /*
             * If you don't set android:syncable="true" in
             * in your <provider> element in the manifest,
             * then call context.setIsSyncable(account, AUTHORITY, 1)
             * here.
             */
        } else {
            /*
             * The account exists or some other error occurred. Log this, report it,
             * or handle it internally.
             */
        }
    }
    ...
}

Добавьте файл метаданных для адаптера синхронизации

Чтобы подключить компонент адаптера синхронизации к платформе, необходимо предоставить платформе метаданные, описывающие компонент и предоставляет дополнительные флаги. Метаданные определяют тип учетных записей, который вы создали для вашего адаптера синхронизации, объявляет авторитетный источник поставщика контента, связанного с вашим приложением, контролирует часть пользовательского интерфейса системы, связанного с адаптером синхронизации, и объявляет, другие флаги связанные с синхронизацией. Объявите метаданные в специальном XML файле, хранящемся в /res/xml/ каталоге в вашем проекте приложения. Вы можете дать любое имя файла, хотя обычно он называется syncadapter.xml.

Этот XML файл содержит один XML элемент <sync-adapter> , который имеет следующие атрибуты:

android:contentAuthority
URI авторитетного источника для вашего поставщика контента. Если вы создали заглушку поставщика контента для вашего приложения в предыдущем уроке Создание заглушки поставщика контента, используйте значение, указанное для атрибута android:authorities в <provider> элементе, который вы добавили в манифест вашего приложения. Более подробно данный атрибут описан в разделе Объявление поставщика в манифесте.
Если вы передаете данные из поставщика контента на сервер с помощью адаптера синхронизации, это значение должно быть таким же, как URI авторитетного источника, который вы используете для этих данных. Это значение также является одним из авторитетный источников, который вы укажете в android:authorities атрибуте <provider> элемента, который объявляет поставщик в вашем манифесте приложения.
android:accountType
Тип учетных записей требуется для платформы синхронизации. Значение должно быть таким же, как значение типа учетной записи указанном при создании файла метаданных аутентификатора, как описано в разделе Добавление аутентификатора в файл метаданных. Это также значение, которое указано для константы ACCOUNT_TYPE во фрагменте кода в разделе Добавить аккаунт необходимый платформой.
Атрибуты настроек
android:userVisible
Устанавливает видимость типа учетных записей адаптера синхронизации. По умолчанию значок учетной записи и подпись, связанные с типом учетных записей, видны в разделе Аккаунты настроек системы, так что вы должны сделать ваш адаптер синхронизации невидимым, кроме случая, когда ваш тип учетной записи или домен легко ассоциируется с вашим приложением. Если вы сделаете ваш тип учетной записи невидимым, вы все еще можете позволить пользователям управлять вашим адаптером синхронизации через пользовательский интерфейс вашего приложения.
android:supportsUploading
Позволяет выгружать данные в облако. Установите в false , если ваше приложение только загружает данные.
android:allowParallelSyncs
Позволяет нескольким экземплярам вашего компонента адаптера синхронизации выполняться параллельно. Используйте это флаг, если ваше приложение поддерживает несколько учетных записей пользователей, и вы хотите, чтобы несколько пользователей передавали данные параллельно. Этот флаг не имеет никакого эффекта, если вы никогда не запускаете несколько передач данных одновременно.
android:isAlwaysSyncable
Указывает платформе синхронизация, что она может работать с адаптером синхронизации в любое время. Если вы хотите программно управлять тем, когда ваш адаптер синхронизации можно запускать, установите этот флаг в false, а затем вызовите requestSync() для запуска адаптера синхронизации. Чтобы узнать больше о запуске адаптера синхронизации см. урок Запуск адаптера синхронизации

Следующий пример показывает XML для адаптера синхронизации, который использует один фиктивные учетные записи и выполняет только скачивание.

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:contentAuthority="com.example.android.datasync.provider"
        android:accountType="com.android.example.datasync"
        android:userVisible="false"
        android:supportsUploading="false"
        android:allowParallelSyncs="false"
        android:isAlwaysSyncable="true"/>

Объявите адаптер синхронизации в манифесте

После того, как вы добавили компонент адаптера синхронизации в ваше приложение, вы должны запросить разрешения, связанные с использованием компонента, и вы должны объявить Service , который вы добавили.

Поскольку компонент адаптера синхронизация запускает код, который передает данные между сетью и устройством, необходимо запросить разрешение на доступ к Интернет. Кроме того, ваше приложение должно запросить разрешение на чтение и запись настроек адаптера синхронизации, так что вы можете управлять адаптером синхронизации программно из других компонентов вашего приложения. Кроме того, необходимо запросить специальное разрешение, которое позволяет вашему приложению использовать компонент аутентификатора, созданный на уроке Создание заглушки аутентификатора.

Чтобы запросить эти разрешения, добавьте следующие строки в ваш манифест приложения в качестве дочерних элементов <manifest>:

android.permission.INTERNET
Позволяет коду адаптера синхронизации получить доступ в Интернет, так что он может скачать или загрузить данные с устройства на сервер. Вам не нужно добавлять это разрешение снова, если вы просили его ранее.
android.permission.READ_SYNC_SETTINGS
Позволяет вашему приложению прочитать текущие настройки адаптера синхронизации. Например, вам нужно это разрешение для того, чтобы вызвать getIsSyncable().
android.permission.WRITE_SYNC_SETTINGS
Позволяет вашему приложению контролировать настройки адаптера синхронизации. Вам нужно это разрешение для того, чтобы установить периодичность выполнения адаптера синхронизации с помощью addPeriodicSync(). Это разрешение не требуется для вызова requestSync(). Чтобы узнать больше о запуске адаптера синхронизации см. Запуск адаптера синхронизации.
android.permission.AUTHENTICATE_ACCOUNTS
Позволяет использовать компонент аутентификатора, созданный на уроке Создание заглушки аутентификатора.

В следующем фрагменте показано, как добавить разрешения:

<manifest>
...
    <uses-permission
            android:name="android.permission.INTERNET"/>
    <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"/>
...
</manifest>

Наконец, чтобы объявить Service , который платформа использует для взаимодействия с адаптером синхронизации, добавьте следующий XML в манифест вашего приложения в качестве дочернего элемента <application>:

        <service
                android:name="com.example.android.datasync.SyncService"
                android:exported="true"
                android:process=":sync">
            <intent-filter>com.example.android.datasync.provider
                <action android:name="android.content.SyncAdapter"/>
            </intent-filter>
            <meta-data android:name="android.content.SyncAdapter"
                    android:resource="@xml/syncadapter" />
        </service>

<intent-filter> элемент настраивает фильтр, который срабатывает для действия android.content.SyncAdapter, посылаемый системой для запуска адаптера синхронизации. Когда фильтр срабатывает, система запускает созданную службу, которая в этом примере называется SyncService. Атрибут android:exported="true" позволяет другим приложениям (в том числе и системе) получить доступ к Service. Атрибут android:process=":sync" говорит системе запустить Service в глобальном общем процессе с именем sync. Если в вашем приложении у вас есть несколько адаптеров синхронизации они могут разделить этот процесс, чтобы снизить накладные расходы.

<meta-data> элемент предоставляет имя XML файла метаданных адаптера синхронизации, созданного ранее. android:name атрибут указывает, что эти метаданные для платформы синхронизации. android:resource элемент задает имя файла метаданных.

Теперь у вас есть все компоненты для вашего адаптера синхронизации. Следующий урок покажет вам, как сказать платформе синхронизации, что нужно запустить адаптер синхронизации, либо в ответ на событие или на основе планирования.