Чому ContentResolver.requestSync не запускає синхронізацію?


112

Я намагаюся реалізувати схему адаптера контент-постачальника-синхронізації, як обговорювалося в 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", і АВТОРІЯ, передана для виклику, відповідає моєму адаптеру syc xml.

Чи є ContentResolver.requestSync правильним способом подати запит на синхронізацію? Схоже, інструмент тестера синхронізації прив’язується безпосередньо до служби та запускає синхронізацію викликів, але, здається, він перемагає мету інтеграції з архітектурою синхронізації.

Якщо це правильний спосіб подати запит на синхронізацію, то чому б працював тестер синхронізації, але не мій дзвінок на ContentResolver.requestSync? Чи є щось, що мені потрібно пропустити в комплекті?

Я тестую емулятор на пристроях під керуванням 2.1 та 2.2.


3
Моя проблема полягала в тому, що до моєї точки злому в адаптері синхронізації не було досягнуто ... Потім я зрозумів, що намагаюся налагодити сервіс ... Сподіваюся, це допомагає іншим, як я.
dangalg

2
Точки перерви в службі адаптера синхронізації не спрацьовують. Це тому, що служба адаптера синхронізації працює в окремому процесі. На це натякав @danglang. Дивіться також це запитання: stackoverflow.com/questions/8559458/…
Костянтин Шуберт

1
У моєму випадку вилучення android:process=":sync"зі служби синхронізації дозволить відладчику вдарити точки дзьоба. Сама служба синхронізації працювала до цього, тому що я міг бачити повідомлення журналу від onPerformSyncметоду від імені іншого процесу.
Сергій

Ще одна причина - вимкнено Wi-Fi, тому якщо ви намагаєтеся синхронізувати дані з глузду, перевірте своє з'єднання.
Allan Veloso

Відповіді:


280

Виклик 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>

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

Встановлення експортованого 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)та повинен повертати a, 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 про те, що адаптер синхронізації, який ми визначили (клас, який викликався в іменному елементі <service>тегу, що включає <meta-data>тег, на який посилається цей файл ...) синхронізує контакти за допомогою облікового запису стилю com.google.

Усі ваші рядки contentAuthority повинні відповідати всім і відповідати тому, що ви синхронізуєте. Це повинен бути рядок, який ви визначаєте, якщо ви створюєте власну базу даних, або якщо ви синхронізуєте відомі, ви повинні використовувати деякі існуючі рядки пристрою типи даних (наприклад, контакти або події календаря чи що у вас є.) Вищенаведене ("com.android.contacts") є рядком ContentAuthority для даних типу контактів (сюрприз, сюрприз.)

accountType також повинен відповідати одному з тих відомих типів облікових записів, які вже введені, або він повинен відповідати тому, який ви створюєте (Це передбачає створення підкласу AccountAuthenticator, щоб отримати автентифікацію на вашому сервері ... Варто статті, самі.) Знову ж таки, "com.google" - це визначений рядок, що ідентифікує ... облікові дані облікового запису в стилі google.com (знову ж, це не повинно бути сюрпризом.)

5. Увімкніть синхронізацію для певної пари облікових записів / ContentAuthority

Нарешті, потрібно активувати синхронізацію. Ви можете зробити це на сторінці "Облікові записи та синхронізація" на панелі керування, перейшовши у свою програму та встановивши прапорець біля свого додатка в межах відповідного облікового запису. Ви також можете це зробити в якомусь інсталяційному коді програми:

ContentResolver.setSyncAutomatically(account, AUTHORITY, true);

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

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

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

Нарешті, ви можете встановити періодичну планову синхронізацію, знову ж таки, за допомогою функцій ContentResolver.

6. Розгляньте наслідки декількох рахунків

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

7. Будьте в курсі ContentResolver.notifyChange ()

Одна хитра річ. ContentResolver.notifyChange()- це функція, яка використовується ContentProviders для сповіщення Android про зміну локальної бази даних. Це виконує дві функції, по-перше, це призведе до того, що курсори, що слідують за цим урі-контентом, оновлюються, а в свою чергу запит і недійсність та перемальовування а ListViewтощо. Це дуже магічно, база даних змінюється, а ваші ListViewпросто оновлюються автоматично. Дивовижно. Крім того, коли база даних зміниться, Android запитає синхронізацію для вас, навіть поза звичайним графіком, щоб ці зміни зняли з пристрою та синхронізували з сервером якомога швидше. Також дивовижно.

Однак є один крайній випадок. Якщо ви вийдете з сервера і натисніть на оновлення до ContentProvider, воно покірно зателефонує, notifyChange()а android піде: "О, зміни бази даних, краще поставте їх на сервер!" (Добре!) Добре написані ContentProvidersпройдуть кілька тестів, щоб побачити, чи зміни відбулися від мережі або від користувача, і встановлять бульовий syncToNetworkпрапор неправдивим, якщо так, щоб запобігти цій марнотратній подвійній синхронізації. Якщо ви вводите дані в A ContentProvider, вам потрібно розібратися, як зробити це робочим. Інакше ви завжди будете виконувати два синхронізації, коли потрібен лише один.

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

Після того, як у вас є всі ці метадані XML на місці та синхронізовано, Android дізнається, як підключити все для вас, і синхронізація повинна почати працювати. У цей момент багато приємних речей просто клацнуть на місці, і це буде відчувати багато, як магія. Насолоджуйтесь!


11
ContentResolver.setSyncAutomatically (акаунт, АВТОРИТЕТ, правда);
jcwenger

1
Немає проблем, радий, що можу допомогти. Знову ж таки, з точки зору стилю, хоча "AUTHORITY" та "myaccounttype" - це фактичні рядки, які ви використовуєте (І не лише зразки для копіювання минулого на сайт), вам обов'язково потрібно очистити свої умови іменування. Ці рядки повинні бути унікальними на всіх пристроях, і ви зіткнетесь із справжніми проблемами, якщо якийсь інший програміст ліниво зробить пакет із відповідним рядком для повноважень і у вас виникне конфлікт. Ура!
jcwenger

22
Ви можете подати запит на синхронізацію, навіть якщо налаштування глобальної синхронізації вимкнено. Просто додайте ContentResolver.SYNC_EXTRAS_MANUALнабір істинних до додатків Пакет, і ви
змусите

2
@kaciula: Я не знаю жодного, але пристрій пам’ятатиме, що його потрібно синхронізувати, і він завершиться, як тільки увімкнеться глобальна синхронізація. Ви дійсно не повинні намагатися сподобати користувача на цьому - Тим більше, що "Глобальна синхронізація вимкнено" - це один з ключових способів економії акумулятора в критичних ситуаціях. Якщо ви дійсно переживаєте, що дані не синхронізуються, розгляньте спливаюче вікно, яке повідомляє користувачеві, чому дані не рухаються, якщо вони сидять певний час. Таким чином ви можете навчити користувачів, які випадково неправильно налаштували свої пристрої, та нагадати користувачам живлення, якщо вони забули.
jcwenger

2
Можливо, варто додати, що якщо ви хочете використовувати addPeriodicSync (), це ТІЛЬКИ, здається, спрацює, якщо ви ТАКОЖ встановилиSyncAutomatically () - я додав це з відчаю, намагаючись змусити НЕКОМУ працювати. Я знаю, що це не було частиною оригінального питання, але це така повна відповідь!
android.weasel

0

Я зателефонував setIsSyncableпісля setAuthTokenметоду AccountManager . Але setAuthTokenповернута функція раніше setIsSyncableбула досягнута. Після зміни замовлення все працювало чудово!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.