Почему ContentResolver.requestSync не вызывает синхронизацию?

Я пытаюсь реализовать шаблон адаптера Content-Provider-Sync, как обсуждалось в Google IO - слайд 26. Мой контент-провайдер работает, и моя синхронизация работает, когда я запускаю ее из приложения Dev Tools Sync Tester, однако, когда я вызываю ContentResolver.requestSync(учетную запись, полномочия, пакет) из моего ContentProvider, моя синхронизация никогда не запускается.

ContentResolver.requestSync(
        account, 
        AUTHORITY, 
        new Bundle());

Изменить - добавлен фрагмент манифеста Мой манифест xml содержит:

<service
    android:name=".sync.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>

- Изменить

Мой файл syncadapter.xml, связанный с моей службой синхронизации, содержит:

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"  
    android:contentAuthority="AUTHORITY"
    android:accountType="myaccounttype"
    android:supportsUploading="true"
/>

Не знаю, какой другой код будет полезен. Учетная запись, переданная requestSync, имеет значение "myaccounttype", а AUTHORITY, переданный вызову, соответствует моему syc-адаптеру xml.

Является ли ContentResolver.requestSync правильным способом запросить синхронизацию? Похоже, что инструмент тестера синхронизации привязывается непосредственно к сервису и вызывает синхронизацию запуска, но похоже, что он побеждает цель интеграции с архитектурой синхронизации.

Если это правильный способ запросить синхронизацию, то зачем нужен тестер синхронизации, но не мой вызов ContentResolver.requestSync? Есть что-то, что мне нужно передать в комплекте?

Я тестирую эмулятор на устройствах с версиями 2.1 и 2.2.

Ответ 1

Вызов requestSync() будет работать только в паре {Account, ContentAuthority}, которая известна системе. Вашему приложению необходимо пройти несколько шагов, чтобы сообщить Android, что вы можете синхронизировать определенный тип контента с использованием определенного типа учетной записи. Он делает это в AndroidManifest.

1. Уведомлять Android о том, что ваш пакет приложений обеспечивает синхронизацию

Во-первых, в AndroidManifest.xml вам необходимо объявить, что у вас есть служба синхронизации:

<service android:name=".sync.mySyncService" android:exported="true">
   <intent-filter>
      <action android:name="android.content.SyncAdapter" /> 
    </intent-filter>
    <meta-data 
        android:name="android.content.SyncAdapter" 
        android:resource="@xml/sync_myapp" /> 
</service>

Атрибут name тега <service> - это имя вашего класса для подключения синхронизации... Я поговорю с этим через секунду.

Параметр exported true делает видимым для других компонентов (необходимо, чтобы ContentResolver мог вызвать его).

Фильтр намерений позволяет поймать намерение, запрашивающее синхронизацию. (Это Intent происходит от ContentResolver, когда вы вызываете ContentResolver.requestSync() или связанные с ним методы планирования.)

Тег <meta-data> будет рассмотрен ниже.

2. Предоставьте Android службу, используемую для поиска SyncAdapter

Итак, сам класс... Вот пример:

public class mySyncService extends Service {

    private static mySyncAdapter mSyncAdapter = null;

    public SyncService() {
        super();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (mSyncAdapter == null) {
            mSyncAdapter = new mySyncAdapter(getApplicationContext(), true);
        }
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return mSyncAdapter.getSyncAdapterBinder();
    }
}

Ваш класс должен расширять Service или один из его подклассов, должен реализовывать public IBinder onBind(Intent) и должен возвращать SyncAdapterBinder при вызове... Вам нужна переменная типа AbstractThreadedSyncAdapter. Итак, как вы можете видеть, это почти все в этом классе. Единственная причина, по которой он заключается в предоставлении Сервиса, который предлагает стандартный интерфейс для Android, чтобы запросить ваш класс относительно того, что представляет собой ваш SyncAdapter.

3. Предоставьте class SyncAdapter, чтобы выполнить синхронизацию.

mySyncAdapter - это место, где хранится реальная логика синхронизации. Его метод onPerformSync() вызывается, когда ему нужно синхронизировать время. Я полагаю, что у вас уже есть это место.

4. Установите привязку между типом учетной записи и полномочным органом по контенту

Оглядываясь назад на AndroidManifest, этот странный тег <meta-data> в нашей службе является ключевым элементом, который устанавливает привязку между ContentAuthority и учетной записью. Он внешне ссылается на другой файл xml (назовите его, как вам нравится, что-то, что подходит вашему приложению). Посмотрите на sync_myapp.xml:

<?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="com.google" 
    android:userVisible="true" /> 

Хорошо, так что это делает? Он сообщает Android, что адаптер синхронизации, который мы определили (класс, который был вызван в элементе name тега <service>, который включает тег <meta-data>, который ссылается на этот файл...), синхронизирует контакты с помощью com. аккаунт в стиле Google.

Все ваши строки contentAuthority должны соответствовать всем и соответствовать тому, что вы синхронизируете. Это должна быть строка, которую вы определяете, если вы создаете свою собственную базу данных, или вы должны использовать некоторые существующие строки устройств, повторно синхронизировать известные типы данных (например, контакты или события календаря или какие у вас есть). Вышеупомянутая ( "com.android.contacts" ) является строкой ContentAuthority для данных типа контактов (неожиданно, неожиданно).

accountType также должен соответствовать одному из тех известных типов учетных записей, которые уже введены, или он должен соответствовать тому, который вы создаете (это включает создание подкласса AccountAuthenticator для получения авторизации на вашем сервере... Стоит статья, сам.) Опять же, "com.google" - это определенная строка, идентифицирующая... учетные данные учетной записи стиля google.com(опять же, это не должно быть сюрпризом.)

5. Включить синхронизацию по данной учетной записи /ContentAuthority

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

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

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

Если синхронизация вашей учетной записи/полномочий или глобальная синхронизация отключены, вызов функции RequestSync() имеет эффект. Он устанавливает флаг, который была запрошена синхронизацией, и будет выполняться, как только включена синхронизация.

Кроме того, за mgv установка ContentResolver.SYNC_EXTRAS_MANUAL в true в пакете дополнительных компонентов вашего запросаSync попросит андроид принудительно синхронизировать, даже если глобальная синхронизация отключена ( уважайте своего пользователя здесь!)

Наконец, вы можете настроить периодическую синхронизацию по расписанию, опять же с функциями ContentResolver.

6. Рассмотрите возможность использования нескольких учетных записей

Возможно иметь несколько учетных записей одного и того же типа (две учетные записи @gmail.com, настроенные на одном устройстве или две учетные записи facebook или две учетные записи twitter и т.д.). Вы должны учитывать последствия применения делая это... Если у вас есть две учетные записи, вы, вероятно, не хотите пытаться синхронизировать их оба в те же таблицы базы данных. Возможно, вам нужно указать, что за один раз может быть активен только один, и очищать таблицы и повторно синхронизировать, если вы переключаете учетные записи. (через страницу свойств, которая запрашивает, какие учетные записи присутствуют). Возможно, вы создаете другую базу данных для каждой учетной записи, возможно, разные таблицы, возможно, ключевой столбец в каждой таблице. Все приложения специфичны и достойны некоторой мысли. ContentResolver.setIsSyncable(Account account, String authority, int syncable) может представлять интерес. setSyncAutomatically() управляет проверкой или снятием флажка учетной/властной пары, в то время как setIsSyncable() предоставляет возможность снять и вычеркнуть строку, чтобы пользователь не мог включить ее. Вы можете установить одну учетную запись Syncable, а другую - не синхронизировать (dsabled).

7. Имейте в виду ContentResolver.notifyChange()

Одна сложная штука. ContentResolver.notifyChange() - это функция, используемая ContentProvider для уведомления Android о том, что локальная база данных была изменена. Это служит для двух функций: во-первых, это приведет к тому, что курсоры следуют за этим содержанием uri для обновления и, в свою очередь, будут требовать и отменять и перерисовывать ListView и т.д. Это очень волшебное, изменения в базе данных и ваш ListView просто автоматически обновляется, Потрясающие. Кроме того, когда база данных изменится, Android запросит синхронизацию для вас, даже вне вашего обычного расписания, чтобы эти изменения были сняты с устройства и синхронизировались с сервером как можно быстрее. Также потрясающе.

Есть один краевой случай. Если вы выйдете с сервера и нажмете обновление в ContentProvider, он будет покорно называть notifyChange(), и андроид пойдет: "О, изменения в базе данных, лучше поместите их на сервер!" (Doh!) Хорошо написанный ContentProviders будет иметь некоторые тесты, чтобы узнать, произошли ли изменения из сети или от пользователя, и установите флаг boolean syncToNetwork false, если это так, чтобы предотвратить эту расточительную двойную синхронизацию. Если вы подаете данные в ContentProvider, вам нужно выяснить, как это сделать. В противном случае вы будете всегда выполнять две синхронизации, если требуется только один.

8. Почувствуйте себя счастливым!

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